Skip to content

Commit e979950

Browse files
committed
泛型 - 简单泛型第一部分
1 parent c289c8d commit e979950

File tree

1 file changed

+88
-1
lines changed

1 file changed

+88
-1
lines changed

docs/book/20-Generics.md

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)