Java泛型在运行时被擦除,仅保留Object类型,可通过反射调用原始add等方法插入任意类型对象,但遍历时强转会触发ClassCastException。

Java 泛型本身不支持运行时类型约束保留,所谓“强制擦除类型约束红线”,本质是利用泛型在编译后已被擦除的特性,绕过编译期检查,在运行时通过反射调用原始方法插入任意类型对象。
理解泛型擦除是前提
Java 泛型是“伪泛型”:所有泛型信息(如 List<String> 中的 String)都在编译阶段被擦除。擦除规则如下:
- 无界类型参数(如 T)统一替换为 Object
- 有界类型参数(如 T extends Number)替换为上界(Number)
- 集合内部实际操作的是 Object 数组或 Object 参数,例如 ArrayList<Integer>.add() 编译后签名就是 add(Object)
反射调用原始 add/remove 方法
既然运行时集合只认 Object,那就可以跳过泛型检查,直接调用其原始 public 方法:
- 获取集合类的 Class 对象(如 list.getClass())
- 用 getDeclaredMethod("add", Object.class) 获取原始 add 方法(不是泛型重载)
- 用 invoke() 传入任意类型实例("hello"、new Date() 等)
此时不会报错——因为 JVM 看到的只是 ArrayList.add(Object),和泛型声明无关。
注意擦除后的类型安全失效点
擦除后类型约束消失,但风险随之而来:
- 遍历时若仍按原泛型类型强转(如 (String) obj),会触发 ClassCastException
- 集合的 get() 返回仍是 Object,需手动转型,且无编译提醒
- IDE 和编译器无法预警,错误仅在运行时暴露
反射也能读取部分泛型声明信息
虽然运行时集合实例不保留泛型,但某些位置的泛型签名仍保留在字节码中,可被反射读取:
- 字段声明(如 private List<String> names;)→ 用 Field.getGenericType() 可拿到 ParameterizedType 并提取 String
- 方法返回值或参数(如 public Map<String, Integer> getData())→ 用 Method.getGenericReturnType() 或 getGenericParameterTypes()
这不能改变运行时行为,但可用于框架做类型推导或序列化适配。

















