11[ TOC]
22
33<!-- Generics -->
4+
45# 第二十章 泛型
56
67> 普通的类和方法只能使用特定的类型:基本数据类型或类类型。如果编写的代码需要应用于多种类型,这种严苛的限制对代码的束缚就会很大。
78
8- 多态是一种面向对象思想的泛化机制。你可以将方法的参数类型设为基类,这样的方法就可以接受任何派生类作为参数,包括暂时还不存在的类。这样的方法更通用,应用范围更广。在类内部也是如此,在任何使用特定类型的地方,基类意味着更大的灵活性。除了` final ` 类(或只提供私有构造函数的类)任何类型都可被扩展,所以大部分时候这种灵活性是自带的。
9+ 多态是一种面向对象思想的泛化机制。你可以将方法的参数类型设为基类,这样的方法就可以接受任何派生类作为参数,包括暂时还不存在的类。这样的方法更通用,应用范围更广。在类内部也是如此,在任何使用特定类型的地方,基类意味着更大的灵活性。除了 ` final ` 类(或只提供私有构造函数的类)任何类型都可被扩展,所以大部分时候这种灵活性是自带的。
910
1011拘泥于单一的继承体系太过局限,因为只有继承体系中的对象才能适用基类作为参数的方法中。如果方法以接口而不是类作为参数,限制就宽松多了,只要实现了接口就可以。这给予调用方一种选项,通过调整现有的类来实现接口,满足方法参数要求。接口可以突破继承体系的限制。
1112
2324
2425Java 的设计者曾说过,这门语言的灵感主要来自 C++ 。尽管如此,学习 Java 时基本不用参考 C++ 。
2526
26- 但是,Java 中的泛型需要与 C++ 进行对比,理由有两个:首先,理解 C++ * 模板* (泛型的主要灵感来源,包括基本语法)的某些特性,有助于理解泛型的基础理念。同时,非常重要的一点是,你可以了解 Java 泛型的局限是什么,以及为什么会有这些局限。最终的目标是明确 Java 泛型的边界,让你成为一个程序高手。只有知道了某个技术不能做什么,你才能更好的做到所能做的 (部分原因是,不必浪费时间在死胡同里)。
27+ 但是,Java 中的泛型需要与 C++ 进行对比,理由有两个:首先,理解 C++ * 模板* (泛型的主要灵感来源,包括基本语法)的某些特性,有助于理解泛型的基础理念。同时,非常重要的一点是,你可以了解 Java 泛型的局限是什么,以及为什么会有这些局限。最终的目标是明确 Java 泛型的边界,让你成为一个程序高手。只有知道了某个技术不能做什么,你才能更好地做到所能做的 (部分原因是,不必浪费时间在死胡同里)。
2728
2829第二个原因是,在 Java 社区中,大家普遍对 C++ 模板有一种误解,而这种误解可能会令你在理解泛型的意图时产生偏差。
2930
3031因此,本章中会介绍少量 C++ 模板的例子,仅当它们确实可以加深理解时才会引入。
3132
3233<!-- Simple Generics -->
34+
3335## 简单泛型
3436
35- 促成泛型出现的最主要的动机之一是为了创建* 集合类* ,参见[ 集合] ( ./12-Collections.md ) 章节。集合用于存放要使用对象 。数组也是如此,不过集合比数组更加灵活,功能更丰富。几乎所有程序在运行过程中都会涉及到一组对象,因此集合是可复用性最高的类库之一。
37+ 促成泛型出现的最主要的动机之一是为了创建* 集合类* ,参见[ 集合] ( ./12-Collections.md ) 章节。集合用于存放要使用到的对象 。数组也是如此,不过集合比数组更加灵活,功能更丰富。几乎所有程序在运行过程中都会涉及到一组对象,因此集合是可复用性最高的类库之一。
3638
3739我们先看一个只能持有单个对象的类。这个类可以明确指定其持有的对象的类型:
3840
@@ -48,7 +50,7 @@ public class Holder1 {
4850}
4951```
5052
51- 这个类的可复用性不高,它无法持有其他类型的对象。我们可不希望为碰到每个类型都编写一个新的类 。
53+ 这个类的可复用性不高,它无法持有其他类型的对象。我们可不希望为碰到的每个类型都编写一个新的类 。
5254
5355在 Java 5 之前,我们可以让这个类直接持有 ` Object ` 类型的对象:
5456
@@ -116,15 +118,15 @@ public class Diamond<T> {
116118
117119注意,在 ` h3 ` 的定义处,` = ` 右边的尖括号是空的(称为“钻石语法”),而不是重复左边的类型信息。在本书剩余部分都会使用这种语法。
118120
119- 一般来说,你可以认为泛型和其他类型差不多,只不过它们碰巧有类型参数罢了。在使用泛型时,你只需要指定类型参数即可 。
121+ 一般来说,你可以认为泛型和其他类型差不多,只不过它们碰巧有类型参数罢了。在使用泛型时,你只需要指定它们的名称和类型参数列表即可 。
120122
121123### 一个元组类库
122124
123- 有时一个方法需要能返回多个对象。而 ** return** 语句只能返回单个对象,解决方法就是创建一个容器对象 ,用它打包想要返回的多个对象。当然,可以在每次需要的时候,专门创建一个类来完成这样的工作。但是有了泛型,我们就可以一劳永逸。同时,还获得了编译时的类型安全。
125+ 有时一个方法需要能返回多个对象。而 ** return** 语句只能返回单个对象,解决方法就是创建一个对象 ,用它打包想要返回的多个对象。当然,可以在每次需要的时候,专门创建一个类来完成这样的工作。但是有了泛型,我们就可以一劳永逸。同时,还获得了编译时的类型安全。
124126
125- 这个概念称为* 元组* ,它是将一组对象直接打包存储于其中的一个单一对象。可以从容器对象读取其中的元素 ,但不允许向其中存储新对象(这个概念也称为 * 数据传输对象* 或 * 信使* )。
127+ 这个概念称为* 元组* ,它是将一组对象直接打包存储于单一对象中。可以从该对象读取其中的元素 ,但不允许向其中存储新对象(这个概念也称为 * 数据传输对象* 或 * 信使* )。
126128
127- 通常,元组可以具有任意长度,元组中的对象可以是不同类型的。不过,我们希望能够为每个对象指明其类型 ,并且从元组中读取出来时,能够得到正确的类型。要处理不同长度的问题,我们需要创建多个不同的元组。下面是一个可以存储两个对象的元组:
129+ 通常,元组可以具有任意长度,元组中的对象可以是不同类型的。不过,我们希望能够为每个对象指明类型 ,并且从元组中读取出来时,能够得到正确的类型。要处理不同长度的问题,我们需要创建多个不同的元组。下面是一个可以存储两个对象的元组:
128130
129131``` java
130132// onjava/Tuple2.java
@@ -359,6 +361,7 @@ brown over fox quick quick dog brown The brown lazy brown
359361` RandomList ` 继承了 ` ArrayList ` 的所有方法。本例中只添加了 ` select() ` 这个方法。
360362
361363<!-- Generic Interfaces -->
364+
362365## 泛型接口
363366
364367泛型也可以应用于接口。例如 * 生成器* ,这是一种专门负责创建对象的类。实际上,这是 * 工厂方法* 设计模式的一种应用。不过,当使用生成器创建新的对象时,它不需要任何参数,而工厂方法一般需要参数。生成器无需额外的信息就知道如何创建新对象。
@@ -520,7 +523,7 @@ public class Fibonacci implements Supplier<Integer> {
520523
521524虽然我们在 ` Fibonacci ` 类的里里外外使用的都是 ` int ` 类型,但是其参数类型却是 ` Integer ` 。这个例子引出了 Java 泛型的一个局限性:基本类型无法作为类型参数。不过 Java 5 具备自动装箱和拆箱的功能,可以很方便地在基本类型和相应的包装类之间进行转换。通过这个例子中 ` Fibonacci ` 类对 ` int ` 的使用,我们已经看到了这种效果。
522525
523- 如果还想更进一步,编写一个实现了 ` Iterator ` 的 ` Fibnoacci ` 生成器。我们的一个选择是重写这个类,令其实现 ` Iterator ` 接口。不过,你并不是总能拥有源代码的控制权,并且,除非必须这么做,否则,我们也不愿意重写一个类。而且我们还有另一种选择,就是创建一个 * 适配器* (Adapter) 来实现所需的接口,我们在前面介绍过这个设计模式。
526+ 如果还想更进一步,编写一个实现了 ` Iterable ` 的 ` Fibnoacci ` 生成器。我们的一个选择是重写这个类,令其实现 ` Iterable ` 接口。不过,你并不是总能拥有源代码的控制权,并且,除非必须这么做,否则,我们也不愿意重写一个类。而且我们还有另一种选择,就是创建一个 * 适配器* (Adapter) 来实现所需的接口,我们在前面介绍过这个设计模式。
524527
525528有多种方法可以实现适配器。例如,可以通过继承来创建适配器类:
526529
@@ -564,7 +567,7 @@ extends Fibonacci implements Iterable<Integer> {
5645671 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
565568```
566569
567- 在循环语句中使用 ` IterableFibonacci ` ,必须在构造函数中提供一个边界值,这样 ` hasNext() ` 才知道何时返回 ** false** ,结束循环。
570+ 在 * for-in * 语句中使用 ` IterableFibonacci ` ,必须在构造函数中提供一个边界值,这样 ` hasNext() ` 才知道何时返回 ** false** ,结束循环。
568571
569572<!-- Generic Methods -->
570573
0 commit comments