执行上述 Java 程序清单,每个清单的核心概念、代码要点及运行结果如下:
Listing 1: 基本泛型类
-
概念 :定义了一个泛型类
Gen<T>,其中T是类型参数,在实例化时被具体类型(如Integer、String)替换。 -
要点 :使用泛型后,从
getob()方法获取值时无需进行强制类型转换,编译器会确保类型安全。 -
运行结果 :
Type of T is java.lang.Integer value: 88 Type of T is java.lang.String value: Generics Test
Listing 2: 非泛型类(对比)
-
概念 :
NonGen类使用Object类型来存储数据,以模拟泛型出现前的实现方式。 -
要点 :从
getob()获取值后必须进行显式的强制类型转换(如(Integer)、(String))。代码末尾的iOb = strOb;赋值是合法的(因为都是NonGen类型),但会导致后续的(Integer) iOb.getob()在运行时抛出ClassCastException,这凸显了非泛型代码在类型安全上的缺陷。 -
运行结果 :
Type of ob is java.lang.Integer value: 88 Type of ob is java.lang.String value: Non-Generics Test Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at NonGenDemo.main(NonGenDemo.java:48)
Listing 3: 多类型参数的泛型类
-
概念 :泛型类
TwoGen<T, V>可以声明多个类型参数。 -
运行结果 :
Type of T is java.lang.Integer Type of V is java.lang.String value: 88 value: Generics ```
Listing 4: 有缺陷的泛型类(编译错误)
- 概念 :
Stats<T>试图计算任意类型数组的平均值,但在average()方法中调用nums[i].doubleValue()会导致编译错误,因为编译器无法确保类型T一定有doubleValue()方法。 - 要点:此清单旨在展示无界泛型的局限性,代码本身无法通过编译。
Listing 5: 有界类型参数
-
概念 :通过
Stats<T extends Number>声明了一个有界类型参数,将T限制为Number或其子类(如Integer、Double)。这样在average()方法中就可以安全调用Number类定义的doubleValue()方法。 -
要点 :被注释掉的
Stats<String>部分将无法编译,因为String不是Number的子类,这体现了编译时类型检查的优势。 -
运行结果 :
iob average is 3.0 dob average is 3.3 ```
Listing 6: 通配符参数
-
概念 :在
sameAvg(Stats<?> ob)方法中使用了无界通配符?,表示它可以接受任何泛型类型的Stats对象作为参数。这使得方法可以比较不同类型(如Integer、Double、Float)的Stats对象的平均值。 -
运行结果 :
iob average is 3.0 dob average is 3.3 fob average is 3.0 Averages of iob and dob differ. Averages of iob and fob are the same.
Listing 7: 有界通配符
-
概念 :演示了有界通配符在继承层次结构中的应用。
Coords<T extends TwoD>是一个泛型类。方法showXY(Coords<?> c)使用无界通配符,可接受任何Coords对象。showXYZ(Coords<? extends ThreeD> c)和showAll(Coords<? extends FourD> c)使用上界通配符,分别限制参数必须是Coords<ThreeD或其子类>和Coords<FourD或其子类>。因此,showXYZ(tdlocs)和showAll(tdlocs)的调用被注释掉,因为tdlocs是Coords<TwoD>类型,不满足? extends ThreeD的约束。 -
运行结果 :
Contents of tdlocs. X Y Coordinates: 0 0 7 918 4
1 -23
Contents of fdlocs.
X Y Coordinates:
1 26 8
22 93 -2
X Y Z Coordinates:
1 2 36 8 14
22 9 43 -2 -23
X Y Z T Coordinates:
1 2 3 4
6 8 14 822 9 4 9
3 -2 -23 17 ```
Listing 8: 泛型方法
-
概念 :在普通类
GenMethDemo中定义了一个泛型方法isIn()。方法签名<T extends Comparable<T>, V extends T>声明了类型参数T和V,其中T必须实现Comparable<T>接口,V必须是T或其子类。这使得方法可以检查一个对象是否在特定类型的数组中。 -
要点 :被注释掉的
isIn("two", nums)调用会导致编译错误,因为类型不兼容(String不是Integer的子类,反之亦然),这保证了类型安全。 -
运行结果 :
2 is in nums7 is not in nums two is in strs seven is not in strs ```
Listing 9: 泛型构造函数
-
概念 :在非泛型类
GenCons中,构造函数被声明为泛型:<T extends Number> GenCons(T arg)。这意味着构造函数可以接受任何Number或其子类的参数。 -
运行结果 :
val: 100.0 val: 123.5
Listing 10: 泛型接口
-
概念 :定义了一个泛型接口
MinMax<T extends Comparable<T>>,然后由泛型类MyClass<T extends Comparable<T>>实现它。这确保了存储在MyClass中的对象类型T本身是可比较的,从而能在min()和max()方法中调用compareTo()。 -
运行结果 :
Max value in inums: 8 Min value in inums: 2 Max value in chs: w Min value in chs: b
Listing 11: 原始类型
-
概念 :演示了使用原始类型(
Gen raw)会绕过泛型类型检查,可能导致运行时ClassCastException。原始类型是为了兼容遗留代码,在新代码中应避免使用。 -
要点:示例中展示了多个由于使用原始类型而可能引发运行时错误的赋值和强制转换,这些代码行被注释掉了。
-
运行结果 :
value: 98.6
Listing 12 & 13: 泛型类的继承
-
Listing 12 :泛型类
Gen2<T>继承自Gen<T>,并传递了相同的类型参数T。 -
Listing 13 :泛型子类
Gen2<T, V>扩展了父类的类型参数,增加了第二个类型参数V。 -
运行结果 (Listing 13) :
Value is: 99 ```
Listing 14: 非泛型类作为泛型类的父类
-
概念 :泛型类
Gen<T>可以继承自一个非泛型类NonGen。 -
运行结果 :
Hello 47 ```
Listing 15: 泛型与 instanceof
-
概念 :演示了在泛型类继承层次中使用
instanceof运算符。由于类型擦除,不能在运行时检查泛型类型的具体参数(如Gen2<Integer>),只能使用无界通配符(Gen2<?>)进行检查。 -
运行结果 :
iOb2 is instance of Gen2 iOb2 is instance of Gen strOb is instance of Gen2 strOb is instance of Gen iOb is instance of Gen ```
Listing 16: 重写泛型方法
-
概念 :泛型子类
Gen2<T>可以重写从泛型父类Gen<T>继承的泛型方法getob()。 -
运行结果 :
Gen's getob(): 88
Gen2's getob(): 99
Gen2's getob(): Generics Test ```
Listing 17: 桥方法
-
概念 :当非泛型类继承一个参数化的泛型类(如
Gen2 extends Gen<String>)并重写其方法时,编译器会生成一个合成的"桥方法"来维持多态性和类型安全。用户代码中看到的是重写的String getob()方法。 -
运行结果 :
You called String getob(): Generics Test
Listing 18: 类型擦除导致的模糊性
- 概念 :由于类型擦除,
set(T o)和set(V o)在编译后会变成相同的原始类型签名set(Object o),导致重载模糊,因此这段代码无法编译。
Listing 19: 不能实例化类型参数
- 概念 :不能在泛型类内部使用
new T()来创建类型参数T的实例,因为类型擦除后T变为Object,且编译器无法知道T是否有可访问的构造函数。
Listing 20: 静态上下文中的类型参数
- 概念 :不能在静态变量或静态方法中使用类的类型参数
T,因为静态成员在类级别共享,而类型参数属于实例级别。
Listing 21: 泛型与数组
- 概念 :
- 不能使用
new T[10]创建泛型数组,因为类型擦除后数组的元素类型无法确定。 - 可以将一个已存在数组的引用赋值给泛型数组变量(如
vals = nums;)。 - 不能创建参数化类型数组(如
Gen<Integer>[] gens = new Gen<Integer>[10];)。 - 可以创建无界通配符类型的数组(如
Gen<?>[] gens = new Gen<?>[10];)。
- 不能使用