You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java泛型:前三行代码编译失败后两行成功的原因解析

Java泛型赋值编译问题解析

核心前提:Java泛型的不变性

Java泛型默认是不变的——哪怕Integer是Number的子类,List<Integer>和List<Number>之间也不存在继承关系。编译器严格禁止这种跨类型赋值,目的是提前杜绝运行时的类型转换错误。


前三行编译失败的原因

  1. List<Number> ints = new ArrayList<Integer>();
    要是允许这么写,你可以通过ints.add(new Double(1.0))往列表里加Double对象,但实际容器是ArrayList<Integer>,根本存不了Double,运行时必然抛出ClassCastException。编译器直接拦截这种不安全的赋值。

  2. List<Integer> nums = new ArrayList<Number>();
    实际容器是ArrayList<Number>,可能存了Double、Long等其他Number子类。如果你用Integer num = nums.get(0)取元素,取出来的是Number类型,强转Integer会出错,编译器提前阻止这种风险。

  3. List<Object> nums = new ArrayList<Integer>();
    List<Object>允许添加任意类型的对象(比如String、Date),但实际容器只能存Integer,强行添加非Integer对象会破坏类型安全,所以编译不通过。


后两行编译成功的原因(通配符的协变与逆变)

Java通过通配符在保证类型安全的前提下,给泛型提供了灵活的赋值能力,分为协变和逆变两种场景:

协变:? extends 只读场景

  • List<? extends Number> nums = new ArrayList<Integer>();
    ? extends Number表示这个列表只能存Number或它的子类,且编译器只允许你读取元素(读取结果是Number类型),不允许添加任何具体类型的元素(除了null)——因为编译器无法确定容器实际能接受哪种Number子类。这种只读限制保证了类型安全,所以编译通过。简单说:子类型泛型容器可以赋值给带? extends父类型的变量。

逆变:? super 只写场景

  • List<? super Number> nums = new ArrayList<Object>();
    ? super Number表示这个列表能存Number或它的父类,编译器允许你添加Number及其子类的元素(父类型容器肯定能容纳子类对象),但读取元素时只能得到Object类型。这种只写限制保证了类型安全,所以编译通过。简单说:父类型泛型容器可以赋值给带? super子类型的变量。

泛型工作原理总结

Java泛型是编译期的类型检查机制,通过类型擦除实现(运行时泛型信息会被擦除)。不变性是默认规则,避免了运行时的类型转换错误;通配符则是在安全边界内,让泛型支持协变和逆变,适配不同的读写场景。

内容的提问来源于stack exchange,提问作者D P

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:55:26