为何Java类型系统允许向ArrayList<Integer>中插入String?
为什么Java允许向
ArrayList<Integer>中插入String类型元素? 问题代码
import java.util.*; class Main { public static void main(String[] args) { List<? extends Object> integerList = new ArrayList<Integer>(); List<Object> objectList = (List<Object>) integerList; objectList.add("a"); objectList.add(3); System.out.println(objectList); // 输出:[a, 3] } }
核心原因拆解
1. 编译器对通配符类型的转换检查更宽松
Java泛型是不变的,也就是说List<Integer>并不是List<Object>的子类型,所以直接强制转换会触发编译错误:
List<Object> objectList = (List<Object>) new ArrayList<Integer>(); // 编译错误:不兼容的类型
但通过List<? extends Object>作为中间类型时,情况发生了变化:
- 第一步:
List<? extends Object> integerList = new ArrayList<Integer>()是合法的协变转换——List<? extends T>可以接受任何存储T子类的List,这里Integer是Object的子类,所以编译器允许。 - 第二步:将
List<? extends Object>强制转换为List<Object>时,编译器无法静态验证这个通配符List的具体类型是否真的是List<Object>,因此会允许这个转换,但会生成未检查转换的警告(需开启编译警告才能看到)。这相当于开发者手动绕过了编译器的静态类型检查。
2. 运行时类型擦除的影响
Java泛型仅在编译阶段生效,运行时所有泛型类型信息都会被擦除:
ArrayList<Integer>和ArrayList<Object>在运行时都会被还原为ArrayList,内部存储的元素类型都是Object。因此向其中插入String或Integer都不会触发运行时错误——JVM根本不知道这个List原本被声明为只能存储Integer。
注意:这是不安全的操作
这种写法本质上破坏了泛型的类型安全性,后续如果尝试将objectList转回List<Integer>并取出元素,会立即抛出ClassCastException:
List<Integer> intList = (List<Integer>) objectList; Integer i = intList.get(0); // 运行时抛出ClassCastException,实际元素是String
内容的提问来源于stack exchange,提问作者Jobin Jacob Kavalam
相关产品推荐
相关产品推荐

