java泛型在编译期被类型擦除,运行时无法通过反射获取泛型信息,因字节码中只保留原始类型;反射仅能读取字段、方法签名等声明处残留的泛型元数据,无法从对象实例中提取。

因为 Java 在编译阶段就执行了类型擦除(Type Erasure),泛型信息不会进入运行时,反射自然拿不到。
类型擦除是 JVM 的硬性设计
Java 5 引入泛型时,为兼容老版本 JVM 和字节码规范,选择在编译期把所有泛型参数(如 List<string></string>)替换成原始类型(List),同时插入必要的类型检查和强制转换。生成的字节码里根本不含 <string></string> 这类信息。
- JVM 指令(如
invokevirtual)只认原始类型,无法携带泛型签名 - 类加载器、方法区、JIT 编译器都按擦除后的结构工作,没有预留泛型存储位置
-
new ArrayList<string>()</string>和new ArrayList<integer>()</integer>运行时都是ArrayList.class,内存中只有一个类实例
反射能查的只是“声明位置”的残留线索
反射本身不创造信息,它只能读取字节码里保留下来的元数据。而这些元数据只存在于字段声明、方法签名、父类/接口继承关系等静态位置,不是每个对象都自带泛型标签。
-
Field.getGenericType()可能返回ParameterizedType——前提是这个字段在源码里明确写了private List<user> users;</user> -
Method.getGenericReturnType()能拿到List<string></string>,但这是从方法定义处读的,不是从返回的对象实例里“提取”的 -
Object.getClass().getGenericSuperclass()对new ArrayList<string>() {}</string>有效,是因为匿名子类把父类泛型记进了自己的常量池——这属于“主动存下来”,不是 JVM 默认行为
有些情况根本无解
一旦泛型出现在类型变量(TypeVariable)或泛型方法参数中,运行时确实没有对应数据。
- 比如
<t> T parse(String json)</t>,反射看到的参数类型是T,而T在运行时就是个占位符,没有实际类型 - 局部变量、方法参数、临时集合(如
new ArrayList())都不保留泛型信息——它们没被写进类结构,反射无从下手 - 混淆工具(如 ProGuard)还可能进一步删掉未引用的泛型签名,让本就脆弱的线索彻底消失
这不是反射的缺陷,而是泛型的设计取舍
Java 泛型本质是编译期的“语法糖+类型检查”,目标是安全性和兼容性,不是运行时类型丰富性。想绕过擦除,就得主动配合:用 TypeToken 匿名子类捕获、靠字段/父类声明提供上下文、或手动传入 Type 对象。指望对任意对象调用 getClass() 就拿到 List<string></string>,就像指望问一张照片“你拍的是哪年春天”——它压根没存这信息。










