mirror of
https://github.com/LingCoder/OnJava8.git
synced 2026-08-24 23:13:28 +08:00
泛型 - 简单泛型第一部分
This commit is contained in:
@@ -30,9 +30,96 @@ Java 的设计者曾说过,这门语言的灵感主要来自 C++ 。尽管如
|
||||
因此,本章中会介绍少量 C++ 模板的例子,仅当它们确实可以加深理解时才会引入。
|
||||
|
||||
<!-- Simple Generics -->
|
||||
|
||||
## 简单泛型
|
||||
|
||||
促成泛型出现的最主要的动机之一是为了创建*集合类*,参见[集合](./12-Collections.md)章节。集合用于存放要使用对象。数组也是如此,不过集合比数组更加灵活,功能更丰富。几乎所有程序在运行过程中都会涉及到一组对象,因此集合是可复用性最高的类库之一。
|
||||
|
||||
我们先看一个只能持有单个对象的类。这个类可以明确指定其持有的对象的类型:
|
||||
|
||||
```java
|
||||
// generics/Holder1.java
|
||||
|
||||
class Automobile {}
|
||||
|
||||
public class Holder1 {
|
||||
private Automobile a;
|
||||
public Holder1(Automobile a) { this.a = a; }
|
||||
Automobile get() { return a; }
|
||||
}
|
||||
```
|
||||
|
||||
这个类的可复用性不高,它无法持有其他类型的对象。我们可不希望为碰到每个类型都编写一个新的类。
|
||||
|
||||
在 Java 5 之前,我们可以让这个类直接持有 `Object` 类型的对象:
|
||||
|
||||
```java
|
||||
// generics/ObjectHolder.java
|
||||
|
||||
public class ObjectHolder {
|
||||
private Object a;
|
||||
public ObjectHolder(Object a) { this.a = a; }
|
||||
public void set(Object a) { this.a = a; }
|
||||
public Object get() { return a; }
|
||||
public static void main(String[] args) {
|
||||
ObjectHolder h2 = new ObjectHolder(new Automobile());
|
||||
Automobile a = (Automobile)h2.get();
|
||||
h2.set("Not an Automobile");
|
||||
String s = (String)h2.get();
|
||||
h2.set(1); // 自动装箱为 Integer
|
||||
Integer x = (Integer)h2.get();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
现在,`ObjectHolder` 可以持有任何类型的对象,在上面的示例中,一个 `ObjectHolder` 先后持有了三种不同类型的对象。
|
||||
|
||||
一个集合中存储多种不同类型的对象的情况很少见,通常而言,我们只会用集合存储同一种类型的对象。泛型的主要目的之一就是用来约定集合要存储什么类型的对象,并且通过编译器确保规约得以满足。
|
||||
|
||||
因此,与其使用 `Object` ,我们更希望先指定一个类型占位符,稍后再决定具体使用什么类型。要达到这个目的,需要使用*类型参数*,用尖括号括住,放在类名后面。然后在使用这个类时,再用实际的类型替换此类型参数。在下面的例子中,`T` 就是类型参数:
|
||||
|
||||
```java
|
||||
// generics/GenericHolder.java
|
||||
|
||||
public class GenericHolder<T> {
|
||||
private T a;
|
||||
public GenericHolder() {}
|
||||
public void set(T a) { this.a = a; }
|
||||
public T get() { return a; }
|
||||
public static void main(String[] args) {
|
||||
GenericHolder<Automobile> h3 = new GenericHolder<Automobile>();
|
||||
h3.set(new Automobile()); // 此处有类型校验
|
||||
Automobile a = h3.get(); // 无需类型转换
|
||||
//- h3.set("Not an Automobile"); // 报错
|
||||
//- h3.set(1); // 报错
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
创建 `GenericHolder` 对象时,必须指明要持有的对象的类型,将其置于尖括号内,就像 `main()` 中那样使用。然后,你就只能在 `GenericHolder` 中存储该类型(或其子类,因为多态与泛型不冲突)的对象了。当你调用 `get()` 取值时,直接就是正确的类型。
|
||||
|
||||
这就是 Java 泛型的核心概念:你只需告诉编译器要使用什么类型,剩下的细节交给它来处理。
|
||||
|
||||
你可能注意到 `h3` 的定义非常繁复。在 `=` 左边有 `GenericHolder<Automobile>`, 右边又重复了一次。在 Java 5 中,这种写法被解释成“必要的”,但在 Java 7 中设计者修正了这个问题(新的简写语法随后成为备受欢迎的特性)。以下是简写的例子:
|
||||
|
||||
```java
|
||||
// generics/Diamond.java
|
||||
|
||||
class Bob {}
|
||||
|
||||
public class Diamond<T> {
|
||||
public static void main(String[] args) {
|
||||
GenericHolder<Bob> h3 = new GenericHolder<>();
|
||||
h3.set(new Bob());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意,在 `h3` 的定义处,`=` 右边的尖括号是空的(称为“钻石语法”),而不是重复左边的类型信息。在本书剩余部分都会使用这种语法。
|
||||
|
||||
一般来说,你可以认为泛型和其他类型差不多,只不过它们碰巧有类型参数罢了。在使用泛型时,你只需要指定类型参数即可。
|
||||
|
||||
|
||||
|
||||
<!-- Generic Interfaces -->
|
||||
|
||||
## 泛型接口
|
||||
|
||||
Reference in New Issue
Block a user