为何无法强转List<A>为List<B>,却可强转List<? extends A>为List<B>?
Why can
List<? extends A> be cast to List<B> with a warning instead of an error, while List<A> can't be cast to List<B> at all? 这是个戳中Java泛型核心设计的好问题,咱们掰开揉碎了说:
先搞懂为什么List<A>不能强转成List<B>(强制转换1)
Java的泛型是不变性的——哪怕B继承自A,List<A>和List<B>也被视为完全无关的类型,编译器直接把这种强转判为错误,原因很简单:这种转换必然会破坏类型安全。
假设允许这种转换,你会触发必然的运行时错误:
class A {} class B extends A {} public class Test { public static void main(String[] args) { List<A> listA = new ArrayList<>(); listA.add(new A()); // 往List<A>里加一个纯A对象 // 这行如果允许编译,运行时取元素转B必炸 // List<B> listB = (List<B>) listA; // B b = listB.get(0); // ClassCastException } }
这种从根上就不安全的操作,编译器直接拦死,不给任何机会。
那为什么List<? extends A>转List<B>(强制转换2)只给警告?
List<? extends A>是一个通配符泛型,它的含义是“一个存放A或者A子类对象的列表”。编译器对它的态度不一样,因为这种转换存在合法的可能性:
比如下面这个完全安全的场景:
List<B> realListB = new ArrayList<>(); realListB.add(new B()); List<? extends A> listExtendsA = realListB; // 合法,因为B是A的子类 List<B> castedListB = (List<B>) listExtendsA; // 强转完全正确,原列表本来就是List<B> B b = castedListB.get(0); // 毫无问题
这种情况下强转是安全的,编译器没办法在编译期就确定List<? extends A>背后实际的列表类型到底是不是List<B>,所以它没法直接判为错误——只能给你一个unchecked警告,意思是“我没法验证这个转换的安全性,你自己要对后续的代码负责”。
但就像你示例里的情况,如果List<? extends A>实际指向的是List<A>(里面装的是纯A对象),强转后去用就会出问题——但这种问题只有在运行时才会暴露,编译器在编译期没法提前检测到。
总结一下
- 对
List<A>转List<B>直接报错:因为这种转换没有任何合法安全的场景,必然会破坏类型安全,编译器直接禁止。 - 对
List<? extends A>转List<B>只给警告:因为存在合法安全的转换场景,编译器无法在编译期确认实际类型,所以把风险判断的责任交给开发者,用警告提示你要小心。
内容的提问来源于stack exchange,提问作者user118967
相关产品推荐
相关产品推荐

