Java泛型擦除后仍可通过反射获取声明处的泛型信息:字段、父类继承、方法签名中的泛型实参可由getGenericType()、getGenericSuperclass()、getGenericReturnType()等API提取,但局部变量和new表达式中的泛型信息完全丢失。

Java 的泛型擦除机制意味着:运行时没有泛型类型信息。List 和 List 编译后都是 List,JVM 加载的字节码里不保留 这类标记。但这不等于“完全无迹可寻”——只要泛型信息出现在类结构声明位置(如字段、父类继承、方法签名),它就会保留在字节码的 Signature 属性中,可通过反射提取。
下面说几个真正能用、不踩坑的处理方式:
从字段声明中读取泛型实参
字段若带泛型(比如 private Map),其完整类型在编译时被记录,运行时可用:
-
field.getGenericType()返回ParameterizedType(不是field.getType(),后者只返回Map.class) - 判断后强转:
((ParameterizedType) type).getActualTypeArguments()得到{String.class, User.class} - 注意:通配符(如
? extends Number)需用WildcardType接口进一步调用getUpperBounds()
通过父类继承关系还原泛型类型
子类显式指定父类泛型时(如 class OrderDao extends BaseDao),JVM 会把 BaseDao 作为泛型父类签名写入字节码:
-
this.getClass().getGenericSuperclass()返回ParameterizedType -
getActualTypeArguments()[0]就是Order.class - 这是 MyBatis、Spring Data JPA 自动推断实体类的核心原理
解析方法参数或返回值的嵌套泛型
接口方法若声明了复杂泛型(如 public Response),其签名完整保留: > search()
-
method.getGenericReturnType()获取Response对应的- >
ParameterizedType - 递归解析:先取
Response的实参List,再从中取Product.class - 遇到
TypeVariable(如方法自身的中的T get() T),无法直接还原,需结合类级泛型上下文判断
避免常见误操作
- ❌ 不要用
instanceof List:语法不合法,只能写instanceof List - ❌ 不要依赖局部变量或方法返回值的泛型:
List中的list = new ArrayList(); 完全丢失,反射也拿不到 - ❌ 不要指望
new T()或T[].class:擦除后T不存在,必须显式传入Class或用Supplier替代
本质上,运行时“找回”泛型信息,靠的不是恢复擦除内容,而是提前把类型信息锚定在结构声明处,再用反射去挖。不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











