Java泛型类型擦除疑问:指定Number时两泛型方法调用差异解析
为什么
sampleMethod(List<T>)调用会报错?类型擦除在这里的作用是什么? 一、编译错误的核心原因:泛型的不变性
Java泛型默认遵循不变性规则:即便Integer是Number的子类,List<Integer>也不是List<Number>的子类。
当你显式指定sampleMethod的类型参数T=Number时,该方法要求的参数是List<Number>类型。而你传入的integerList是List<Integer>,这两个类型在编译期被视为完全不兼容的独立类型——编译器会直接拒绝这种赋值,因为这会引发潜在的类型安全问题:比如如果允许这种传入,sampleMethod内部可能会尝试向List<Number>中添加Float类型元素,但实际底层是List<Integer>,运行时会抛出ClassCastException。
二、类型擦除的作用阶段与规则
类型擦除是Java泛型的编译后处理逻辑,编译期的类型检查是在擦除之前完成的,因此第二个案例的错误和擦除无关,是编译期约束检查不通过导致的:
- 对于无边界的泛型方法
<T> void sampleMethod(List<T>),类型擦除后,T会被替换为其默认边界Object,方法签名变为void sampleMethod(List tempList)。但编译时的类型检查严格基于你指定的T=Number,要求传入的参数必须是List<Number>。 - 你显式指定
T=Number后,编译器不会将T替换为Object或其他类型,擦除只是移除泛型语法标记、替换为边界类型,但编译检查完全遵循你指定的泛型约束执行。
三、通配符协变与普通泛型的差异
sampleMethodWC的参数List<? extends T>利用了泛型的协变特性:当T=Number时,List<? extends Number>表示“元素类型是Number或其子类的List”,因此List<Integer>完全符合这个约束——通配符的上界声明保证了从该List读取的元素都可以安全地向上转型为Number,不会有类型安全问题。
而普通泛型List<T>不支持协变,要求元素类型严格匹配T,这就是两者调用结果不同的根本原因。
内容的提问来源于stack exchange,提问作者Dipto Paul Dip
相关产品推荐
相关产品推荐

