
执行上述 Java 程序清单每个清单的核心概念、代码要点及运行结果如下Listing 1: 基本泛型类概念定义了一个泛型类GenT其中T是类型参数在实例化时被具体类型如Integer、String替换。要点使用泛型后从getob()方法获取值时无需进行强制类型转换编译器会确保类型安全。运行结果Type of T is java.lang.Integer value: 88 Type of T is java.lang.String value: Generics TestListing 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: 多类型参数的泛型类概念泛型类TwoGenT, V可以声明多个类型参数。运行结果Type of T is java.lang.Integer Type of V is java.lang.String value: 88 value: Generics Listing 4: 有缺陷的泛型类编译错误概念StatsT试图计算任意类型数组的平均值但在average()方法中调用nums[i].doubleValue()会导致编译错误因为编译器无法确保类型T一定有doubleValue()方法。要点此清单旨在展示无界泛型的局限性代码本身无法通过编译。Listing 5: 有界类型参数概念通过StatsT extends Number声明了一个有界类型参数将T限制为Number或其子类如Integer、Double。这样在average()方法中就可以安全调用Number类定义的doubleValue()方法。要点被注释掉的StatsString部分将无法编译因为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: 有界通配符概念演示了有界通配符在继承层次结构中的应用。CoordsT extends TwoD是一个泛型类。方法showXY(Coords? c)使用无界通配符可接受任何Coords对象。showXYZ(Coords? extends ThreeD c)和showAll(Coords? extends FourD c)使用上界通配符分别限制参数必须是CoordsThreeD或其子类和CoordsFourD或其子类。因此showXYZ(tdlocs)和showAll(tdlocs)的调用被注释掉因为tdlocs是CoordsTwoD类型不满足? extends ThreeD的约束。运行结果Contents of tdlocs. X Y Coordinates: 0 0 7 918 41 -23Contents 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 ComparableT, V extends T声明了类型参数T和V其中T必须实现ComparableT接口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.5Listing 10: 泛型接口概念定义了一个泛型接口MinMaxT extends ComparableT然后由泛型类MyClassT extends ComparableT实现它。这确保了存储在MyClass中的对象类型T本身是可比较的从而能在min()和max()方法中调用compareTo()。运行结果Max value in inums: 8 Min value in inums: 2 Max value in chs: w Min value in chs: bListing 11: 原始类型概念演示了使用原始类型Gen raw会绕过泛型类型检查可能导致运行时ClassCastException。原始类型是为了兼容遗留代码在新代码中应避免使用。要点示例中展示了多个由于使用原始类型而可能引发运行时错误的赋值和强制转换这些代码行被注释掉了。运行结果value: 98.6Listing 12 13: 泛型类的继承Listing 12泛型类Gen2T继承自GenT并传递了相同的类型参数T。Listing 13泛型子类Gen2T, V扩展了父类的类型参数增加了第二个类型参数V。运行结果 (Listing 13)Value is: 99 Listing 14: 非泛型类作为泛型类的父类概念泛型类GenT可以继承自一个非泛型类NonGen。运行结果Hello 47 Listing 15: 泛型与 instanceof概念演示了在泛型类继承层次中使用instanceof运算符。由于类型擦除不能在运行时检查泛型类型的具体参数如Gen2Integer只能使用无界通配符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: 重写泛型方法概念泛型子类Gen2T可以重写从泛型父类GenT继承的泛型方法getob()。运行结果Gens getob(): 88Gen2s getob(): 99Gen2s getob(): Generics Test Listing 17: 桥方法概念当非泛型类继承一个参数化的泛型类如Gen2 extends GenString并重写其方法时编译器会生成一个合成的“桥方法”来维持多态性和类型安全。用户代码中看到的是重写的String getob()方法。运行结果You called String getob(): Generics TestListing 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;。不能创建参数化类型数组如GenInteger[] gens new GenInteger[10];。可以创建无界通配符类型的数组如Gen?[] gens new Gen?[10];。参考来源Java 泛型(Generic) 自定义泛型类和泛型接口和泛型方法的使用、泛型通配符泛型类、泛型方法及泛型应用深入解析 Java 泛型的魅力与机制泛型类和泛型接口关于java中泛型类和泛型方法