Java泛型在编译后被擦除,运行时List<Integer>和List<String>均为List,反射可绕过编译检查调用add(Object)添加任意引用类型,但会破坏类型安全,导致ClassCastException等运行时错误。

Java 的泛型在编译后会被擦除(Type Erasure),运行时 List<Integer> 和 List<String> 都是 List,底层都是 Object[]。因此,**反射本身并不能“恢复”泛型类型信息来让编译器允许类型安全的添加**,但可以绕过编译期检查,在运行时强行往一个声明了泛型的 List(如 List<Integer>)中添加字符串——这属于不安全操作,仅用于理解机制或特殊调试场景,生产代码中应避免。
为什么能绕过?
因为泛型检查只发生在编译期。JVM 运行时所有泛型都被擦除为原始类型(如 List),实际调用的是 add(Object) 方法。只要对象能被转成 Object(一切引用类型都可以),就能通过反射调用成功。
如何用反射往 List<Integer> 中添加字符串
假设你有一个声明为 List<Integer> 的变量,但想在运行时插入字符串:
- 获取该 List 实例(比如
list) - 通过反射获取其
add方法(签名是boolean add(Object)) - 调用该方法,传入字符串(如
"hello")
示例代码:
立即学习“Java免费学习笔记(深入)”;
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
// 反射调用 add(Object)
try {
Method addMethod = list.getClass().getMethod("add", Object.class);
addMethod.invoke(list, "hello"); // ✅ 成功添加,不报错
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(list); // [1, 2, "hello"] —— 混合了 Integer 和 String!
后续访问时的风险
虽然添加成功,但危险才刚开始:
- 遍历时若用
for (Integer x : list),会在取到"hello"时抛ClassCastException -
list.get(2)返回Object,强制转Integer同样会崩溃 - 泛型契约被破坏,静态分析和 IDE 提示失效
补充说明:反射不能“修复”泛型类型检查
有人误以为通过反射能“获取真实泛型类型并据此安全添加”,这是误解。例如:
-
list.getClass().getGenericSuperclass()可能返回ArrayList,它没有泛型参数 - 只有字段、方法返回值、方法参数等位置的泛型声明才可能保留(通过
getGenericType()),而集合实例本身不携带泛型类型信息 - 所以反射无法帮你做“类型校验”,它只是跳过了编译检查


















