泛型类与类型安全详解

执行上述 Java 程序清单,每个清单的核心概念、代码要点及运行结果如下:

Listing 1: 基本泛型类

  • 概念 :定义了一个泛型类 Gen<T>,其中 T 是类型参数,在实例化时被具体类型(如 IntegerString)替换。

  • 要点 :使用泛型后,从 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 或其子类(如 IntegerDouble)。这样在 average() 方法中就可以安全调用 Number 类定义的 doubleValue() 方法。

  • 要点 :被注释掉的 Stats<String> 部分将无法编译,因为 String 不是 Number 的子类,这体现了编译时类型检查的优势。

  • 运行结果

    复制代码
    iob average is 3.0
    dob average is 3.3 ```

Listing 6: 通配符参数

  • 概念 :在 sameAvg(Stats<?> ob) 方法中使用了无界通配符 ?,表示它可以接受任何泛型类型的 Stats 对象作为参数。这使得方法可以比较不同类型(如 IntegerDoubleFloat)的 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) 的调用被注释掉,因为 tdlocsCoords<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> 声明了类型参数 TV,其中 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];)。

参考来源

相关推荐
tiger从容淡定是人生1 小时前
生态入口之战:GPT、Gemini、Copilot与DeepSeek的战略分野
图像处理·人工智能·chatgpt·copilot·软件构建
半兽先生1 小时前
大模型技术开发与应用——4.大模型提示词工程实战
人工智能·算法
太子釢1 小时前
手写一个 RAG:从零搭建可溯源的检索增强问答系统
人工智能
不吃辣4902 小时前
vibe coding | 如何做一个 AI 音乐生成工具?
java·人工智能·后端·ai·ai编程
文人sec2 小时前
从接口压测到全链路质量保障:AI智能客服系统的软件测试实践
人工智能
声讯电子2 小时前
实测A-59F音频模组:降噪稳、不啸叫、通话清晰
人工智能·语音识别·ai降噪·usb接口·回音消除
神奇霸王龙2 小时前
2026旗舰六阶段MCP流水线实战指南
人工智能·ai·ai作画·prompt·aigc·mcp
山河不见老2 小时前
【Cursor 、Qoder安装问题】Cursor 、Qoder安装卡在“正在准备安装”问题排查及解决
人工智能·windows·编辑器
新知图书2 小时前
2.3 开发工作流
人工智能·agent·ai agent·智能体