Java泛型擦除后仍可通过反射获取真实类型,关键是从声明位置(如父类继承、接口实现、字段/方法签名)利用ParameterizedType提取;不能从实例getClass()获取,仅当泛型信息显式写入字节码时才有效。

Java 的泛型在编译后会被擦除(Type Erasure),运行时无法直接通过 obj.getClass().getTypeParameters() 获取泛型实际类型。但**在特定条件下,可以通过反射获取真实泛型参数类型**,关键在于:泛型信息必须在字节码中保留,且需借助 ParameterizedType、GenericSuperclass 或 getGenericInterfaces() 等接口来提取。
从继承关系中获取父类的泛型实参(最常用)
当子类继承/实现一个带具体泛型参数的泛型父类或接口时(如 class MyList extends ArrayList<string></string>),JVM 会在子类的字节码中保留该泛型信息,可通过 getGenericSuperclass() 提取。
- 调用
getClass().getGenericSuperclass(),返回的是ParameterizedType(而非Class) - 强转后调用
getActualTypeArguments(),即可拿到真实的泛型类型数组(如String.class) - 注意:仅适用于「子类明确指定了泛型实参」的情况;若写成
extends ArrayList<t></t>或extends ArrayList,则得不到具体类型
从接口实现中获取泛型实参
类似继承,如果类实现了带具体泛型的接口(如 class A implements Comparable<date></date>),可用 getGenericInterfaces():
- 遍历返回的
Type[],找到对应接口的ParameterizedType - 再调用
getActualTypeArguments()获取实参类型 - 适合用于获取如
Comparable<t></t>、Function<k v></k>等接口的实际类型
通过字段或方法签名获取泛型类型(需有明确声明)
如果某个字段、方法返回值或参数使用了泛型(如 private List<integer> nums;</integer>),可通过反射获取其泛型声明:
立即学习“Java免费学习笔记(深入)”;
- 用
Field.getGenericType()或Method.getGenericReturnType()得到Type - 若为
ParameterizedType,调用getActualTypeArguments()即可 - 注意:仅对源码中显式写出泛型的成员有效;局部变量、方法内泛型无法获取
局限与注意事项
泛型类型擦除是 JVM 规范行为,反射能“绕过”擦除的前提是:泛型信息被编码进字节码并保留在元数据中。以下情况无法获取真实类型:
- 泛型变量声明在方法内部(如
List<string> list = new ArrayList();</string>) - 使用原始类型(
new ArrayList())或未指定泛型(extends ArrayList) - 泛型类型是类型变量(如
<t></t>)而非具体类型(String) - 运行时通过
new TypeToken<list>>() {}</list>这种匿名子类技巧,本质仍是利用上文第一条原理


















