泛型类与类型安全详解

执行上述 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: 泛型与数组

  • 概念 :
    1. 不能使用 new T[10] 创建泛型数组,因为类型擦除后数组的元素类型无法确定。
    2. 可以将一个已存在数组的引用赋值给泛型数组变量(如 vals = nums;)。
    3. 不能创建参数化类型数组(如 Gen<Integer>[] gens = new Gen<Integer>[10];)。
    4. 可以创建无界通配符类型的数组(如 Gen<?>[] gens = new Gen<?>[10];)。

参考来源

相关推荐
小oo呆2 小时前
【自然语言处理与大模型】如何编写优质的Skill?
人工智能
非优秀程序员9 小时前
第12章 烧钱无底洞!!!不烧就死,烧了也不知道啥时候死|《互联网产品恩仇录》
人工智能
Zelman94510 小时前
第06章-交互与观察动作空间
人工智能
冬奇Lab10 小时前
开源项目第258期:Harness² —— Google Research 的「递归式 Agent Harness 优化」,模型权重不动,只改 Agent 的外壳
人工智能·开源
字节跳动视频云技术团队12 小时前
每两家头部手机厂商就有一家选它:火山引擎如何成为 AI 手机助手的“隐形底座”?
人工智能
泯泷12 小时前
像项目经理一样指挥 AI 团队:管理者模式
人工智能·llm·agent
京东云开发者12 小时前
双11囤Token火了:京东搜索量暴涨7倍,AI生产力成新刚需
人工智能
ahccqw12 小时前
OpenCV 车辆数量统计
人工智能·opencv·计算机视觉
人工智能AI技术13 小时前
LangGraph 本地 build 频频翻车,最终在服务器跑通流式对话
人工智能