@@ -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