Skip to content

Commit 977d29d

Browse files
authored
Merge pull request lingcoder#239 from thinker1990/generics
泛型 - 简单泛型小节
2 parents 2f9f785 + f53707a commit 977d29d

1 file changed

Lines changed: 139 additions & 0 deletions

File tree

docs/book/20-Generics.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,146 @@ public class Diamond<T> {
118118

119119
一般来说,你可以认为泛型和其他类型差不多,只不过它们碰巧有类型参数罢了。在使用泛型时,你只需要指定类型参数即可。
120120

121+
### 一个元组类库
121122

123+
有时一个方法需要能返回多个对象。而 **return** 语句只能返回单个对象,解决方法就是创建一个容器对象,用它打包想要返回的多个对象。当然,可以在每次需要的时候,专门创建一个类来完成这样的工作。但是有了泛型,我们就可以一劳永逸。同时,还获得了编译时的类型安全。
124+
125+
这个概念称为*元组*,它是将一组对象直接打包存储于其中的一个单一对象。可以从容器对象读取其中的元素,但不允许向其中存储新对象(这个概念也称为 *数据传输对象**信使* )。
126+
127+
通常,元组可以具有任意长度,元组中的对象可以是不同类型的。不过,我们希望能够为每个对象指明其类型,并且从元组中读取出来时,能够得到正确的类型。要处理不同长度的问题,我们需要创建多个不同的元组。下面是一个可以存储两个对象的元组:
128+
129+
```java
130+
// onjava/Tuple2.java
131+
package onjava;
132+
133+
public class Tuple2<A, B> {
134+
public final A a1;
135+
public final B a2;
136+
public Tuple2(A a, B b) { a1 = a; a2 = b; }
137+
public String rep() { return a1 + ", " + a2; }
138+
@Override
139+
public String toString() {
140+
return "(" + rep() + ")";
141+
}
142+
}
143+
```
144+
145+
构造函数传入要存储的对象。这个元组隐式地保持了其中元素的次序。
146+
147+
初次阅读上面的代码时,你可能认为这违反了 Java 编程的封装原则。`a1``a2` 应该声明为 **private**,然后提供 `getFirst()``getSecond()` 取值方法才对呀?考虑下这样做能提供的“安全性”是什么:元组的使用程序可以读取 `a1``a2` 然后对它们执行任何操作,但无法对 `a1``a2` 重新赋值。例子中的 `final` 可以实现同样的效果,并且更为简洁明了。
148+
149+
另一种设计思路是允许元组的用户给 `a1``a2` 重新赋值。然而,采用上例中的形式无疑更加安全,如果用户想存储不同的元素,就会强制他们创建新的 `Tuple2` 对象。
150+
151+
我们可以利用继承机制实现长度更长的元组。添加更多的类型参数就行了:
152+
153+
```java
154+
// onjava/Tuple3.java
155+
package onjava;
156+
157+
public class Tuple3<A, B, C> extends Tuple2<A, B> {
158+
public final C a3;
159+
public Tuple3(A a, B b, C c) {
160+
super(a, b);
161+
a3 = c;
162+
}
163+
@Override
164+
public String rep() {
165+
return super.rep() + ", " + a3;
166+
}
167+
}
168+
169+
170+
// onjava/Tuple4.java
171+
package onjava;
172+
173+
public class Tuple4<A, B, C, D>
174+
extends Tuple3<A, B, C> {
175+
public final D a4;
176+
public Tuple4(A a, B b, C c, D d) {
177+
super(a, b, c);
178+
a4 = d;
179+
}
180+
@Override
181+
public String rep() {
182+
return super.rep() + ", " + a4;
183+
}
184+
}
185+
186+
187+
// onjava/Tuple5.java
188+
package onjava;
189+
190+
public class Tuple5<A, B, C, D, E>
191+
extends Tuple4<A, B, C, D> {
192+
public final E a5;
193+
public Tuple5(A a, B b, C c, D d, E e) {
194+
super(a, b, c, d);
195+
a5 = e;
196+
}
197+
@Override
198+
public String rep() {
199+
return super.rep() + ", " + a5;
200+
}
201+
}
202+
```
203+
204+
演示需要,再定义两个类:
205+
206+
```java
207+
// generics/Amphibian.java
208+
public class Amphibian {}
209+
210+
// generics/Vehicle.java
211+
public class Vehicle {}
212+
```
213+
214+
使用元组时,你只需要定义一个长度适合的元组,将其作为返回值即可。注意下面例子中方法的返回类型:
215+
216+
```java
217+
// generics/TupleTest.java
218+
import onjava.*;
219+
220+
public class TupleTest {
221+
static Tuple2<String, Integer> f() {
222+
// 47 自动装箱为 Integer
223+
return new Tuple2<>("hi", 47);
224+
}
225+
226+
static Tuple3<Amphibian, String, Integer> g() {
227+
return new Tuple3<>(new Amphibian(), "hi", 47);
228+
}
229+
230+
static Tuple4<Vehicle, Amphibian, String, Integer> h() {
231+
return new Tuple4<>(new Vehicle(), new Amphibian(), "hi", 47);
232+
}
233+
234+
static Tuple5<Vehicle, Amphibian, String, Integer, Double> k() {
235+
return new Tuple5<>(new Vehicle(), new Amphibian(), "hi", 47, 11.1);
236+
}
237+
238+
public static void main(String[] args) {
239+
Tuple2<String, Integer> ttsi = f();
240+
System.out.println(ttsi);
241+
// ttsi.a1 = "there"; // 编译错误,因为 final 不能重新赋值
242+
System.out.println(g());
243+
System.out.println(h());
244+
System.out.println(k());
245+
}
246+
}
247+
248+
/* 输出:
249+
(hi, 47)
250+
(Amphibian@1540e19d, hi, 47)
251+
(Vehicle@7f31245a, Amphibian@6d6f6e28, hi, 47)
252+
(Vehicle@330bedb4, Amphibian@2503dbd3, hi, 47, 11.1)
253+
*/
254+
```
255+
256+
有了泛型,你可以很容易地创建元组,令其返回一组任意类型的对象。
257+
258+
通过 `ttsi.a1 = "there"` 语句的报错,我们可以看出,**final** 声明确实可以确保 **public** 字段在对象被构造出来之后就不能重新赋值了。
259+
260+
在上面的程序中,`new` 表达式有些啰嗦。本章稍后会介绍,如何利用 *泛型方法* 简化它们。
122261

123262
<!-- Generic Interfaces -->
124263

0 commit comments

Comments
 (0)