@@ -30,9 +30,96 @@ Java 的设计者曾说过,这门语言的灵感主要来自 C++ 。尽管如
3030因此,本章中会介绍少量 C++ 模板的例子,仅当它们确实可以加深理解时才会引入。
3131
3232<!-- Simple Generics -->
33-
3433## 简单泛型
3534
35+ 促成泛型出现的最主要的动机之一是为了创建* 集合类* ,参见[ 集合] ( ./12-Collections.md ) 章节。集合用于存放要使用对象。数组也是如此,不过集合比数组更加灵活,功能更丰富。几乎所有程序在运行过程中都会涉及到一组对象,因此集合是可复用性最高的类库之一。
36+
37+ 我们先看一个只能持有单个对象的类。这个类可以明确指定其持有的对象的类型:
38+
39+ ``` java
40+ // generics/Holder1.java
41+
42+ class Automobile {}
43+
44+ public class Holder1 {
45+ private Automobile a;
46+ public Holder1 (Automobile a ) { this . a = a; }
47+ Automobile get () { return a; }
48+ }
49+ ```
50+
51+ 这个类的可复用性不高,它无法持有其他类型的对象。我们可不希望为碰到每个类型都编写一个新的类。
52+
53+ 在 Java 5 之前,我们可以让这个类直接持有 ` Object ` 类型的对象:
54+
55+ ``` java
56+ // generics/ObjectHolder.java
57+
58+ public class ObjectHolder {
59+ private Object a;
60+ public ObjectHolder (Object a ) { this . a = a; }
61+ public void set (Object a ) { this . a = a; }
62+ public Object get () { return a; }
63+ public static void main (String [] args ) {
64+ ObjectHolder h2 = new ObjectHolder (new Automobile ());
65+ Automobile a = (Automobile )h2. get();
66+ h2. set(" Not an Automobile" );
67+ String s = (String )h2. get();
68+ h2. set(1 ); // 自动装箱为 Integer
69+ Integer x = (Integer )h2. get();
70+ }
71+ }
72+ ```
73+
74+ 现在,` ObjectHolder ` 可以持有任何类型的对象,在上面的示例中,一个 ` ObjectHolder ` 先后持有了三种不同类型的对象。
75+
76+ 一个集合中存储多种不同类型的对象的情况很少见,通常而言,我们只会用集合存储同一种类型的对象。泛型的主要目的之一就是用来约定集合要存储什么类型的对象,并且通过编译器确保规约得以满足。
77+
78+ 因此,与其使用 ` Object ` ,我们更希望先指定一个类型占位符,稍后再决定具体使用什么类型。要达到这个目的,需要使用* 类型参数* ,用尖括号括住,放在类名后面。然后在使用这个类时,再用实际的类型替换此类型参数。在下面的例子中,` T ` 就是类型参数:
79+
80+ ``` java
81+ // generics/GenericHolder.java
82+
83+ public class GenericHolder <T> {
84+ private T a;
85+ public GenericHolder () {}
86+ public void set (T a ) { this . a = a; }
87+ public T get () { return a; }
88+ public static void main (String [] args ) {
89+ GenericHolder<Automobile > h3 = new GenericHolder<Automobile > ();
90+ h3. set(new Automobile ()); // 此处有类型校验
91+ Automobile a = h3. get(); // 无需类型转换
92+ // - h3.set("Not an Automobile"); // 报错
93+ // - h3.set(1); // 报错
94+ }
95+ }
96+ ```
97+
98+ 创建 ` GenericHolder ` 对象时,必须指明要持有的对象的类型,将其置于尖括号内,就像 ` main() ` 中那样使用。然后,你就只能在 ` GenericHolder ` 中存储该类型(或其子类,因为多态与泛型不冲突)的对象了。当你调用 ` get() ` 取值时,直接就是正确的类型。
99+
100+ 这就是 Java 泛型的核心概念:你只需告诉编译器要使用什么类型,剩下的细节交给它来处理。
101+
102+ 你可能注意到 ` h3 ` 的定义非常繁复。在 ` = ` 左边有 ` GenericHolder<Automobile> ` , 右边又重复了一次。在 Java 5 中,这种写法被解释成“必要的”,但在 Java 7 中设计者修正了这个问题(新的简写语法随后成为备受欢迎的特性)。以下是简写的例子:
103+
104+ ``` java
105+ // generics/Diamond.java
106+
107+ class Bob {}
108+
109+ public class Diamond <T> {
110+ public static void main (String [] args ) {
111+ GenericHolder<Bob > h3 = new GenericHolder<> ();
112+ h3. set(new Bob ());
113+ }
114+ }
115+ ```
116+
117+ 注意,在 ` h3 ` 的定义处,` = ` 右边的尖括号是空的(称为“钻石语法”),而不是重复左边的类型信息。在本书剩余部分都会使用这种语法。
118+
119+ 一般来说,你可以认为泛型和其他类型差不多,只不过它们碰巧有类型参数罢了。在使用泛型时,你只需要指定类型参数即可。
120+
121+
122+
36123<!-- Generic Interfaces -->
37124
38125## 泛型接口
0 commit comments