Java泛型在运行时因类型擦除而丢失所有类型参数,Class对象、实例及数组均不保留泛型信息;反射仅能获取类声明中的泛型元数据(如字段、父类、方法签名),无法获知具体实例的类型参数。

Java 泛型在编译后会经历类型擦除(Type Erasure),这意味着泛型信息仅存在于源码和编译后的字节码的 Signature 属性中,而**运行时的 Class 对象、对象实例、数组类型等均不保留泛型的实际类型参数**。因此,反射无法获取泛型的具体类型(如 List<string></string> 中的 String),根本原因不是反射能力不足,而是该信息在 JVM 运行期根本不存在。
类型擦除到底擦掉了什么
泛型类型参数(如 <t></t>、<e></e>、<k></k>)在编译阶段被替换为:
- 其限定上限(
extends后的类型),若无则替换为Object; - 所有泛型类型声明(如
List<string></string>)被“降级”为原始类型(List); - 泛型方法的类型参数也被擦除,仅保留桥接方法(bridge method)供多态调用。
例如:ArrayList<integer> list = new ArrayList();</integer> 编译后等价于 ArrayList list = new ArrayList(); —— 运行时 list.getClass() 返回的就是 class java.util.ArrayList,没有 <integer></integer> 的任何痕迹。
为什么反射 getGenericXxx 方法看似能“看到”泛型
某些反射 API(如 Field.getGenericType()、Method.getGenericReturnType()、Class.getGenericSuperclass())确实能返回 Type 子类(如 ParameterizedType),但这只适用于泛型信息被显式保留在字节码元数据中且未被实例化擦除的场景,典型包括:
立即学习“Java免费学习笔记(深入)”;
- 字段声明为
private List<string> names;</string>→ 可通过Field.getGenericType()获取ParameterizedType,从中提取String; - 父类是泛型类,如
class MyMap extends HashMap<string integer> {}</string>→MyMap.class.getGenericSuperclass()可得HashMap<string></string>; - 方法签名含泛型,如
<t> T getValue()</t>→Method.getGenericReturnType()返回TypeVariable。
但注意:这些信息来自类文件的 Signature 属性或泛型声明结构,与运行时实际创建的对象无关。一旦涉及具体实例(如 new ArrayList<date>()</date>),其类型参数就彻底丢失,反射查不到该实例“本应装什么”。
运行时真正不可知的典型例子
以下代码在运行时无法区分泛型实际类型:
List<String> a = new ArrayList<>(); List<Integer> b = new ArrayList<>(); System.out.println(a.getClass() == b.getClass()); // true → 都是 ArrayList System.out.println(a instanceof List<String>); // 编译错误 → 泛型不能用于 instanceof
即使使用反射:
-
a.getClass().getTypeParameters()返回空数组(ArrayList类本身不是泛型类,是原始类型); -
a.getClass().getGenericInterfaces()返回Collection<e></e>等,其中E是TypeVariable,不代表a当前用了String; - 没有任何 API 能回答“这个
ArrayList实例当初是用哪个泛型参数构造的?”——因为答案在运行时不存在。
绕过擦除的常见实践(非反射方案)
若真需运行时泛型信息,必须主动保留,常见方式有:
-
类型令牌(TypeToken):如 Gson 或 Guava 中的
new TypeToken<list>>() {}</list>,利用匿名子类的getGenericSuperclass()捕获泛型; -
显式传入 Class 对象:如
fromJson(json, String.class)或new MyContainer<string>(String.class)</string>; -
运行时检查元素:对集合遍历取
element.getClass()(仅适用于非空且类型统一的集合,不严谨但实用)。
这些都不是“恢复擦除”,而是通过额外机制补偿擦除带来的信息缺失。


















