java泛型擦除后,可通过反射从类结构定义处提取泛型信息:字段/方法的getgenerictype()、typereference匿名子类、显式传入type对象,配合proguard保留signature等属性。

Java 泛型擦除后,泛型信息本身不会自动保留,但编译器会把原始泛型声明写入 .class 文件的 Signature 属性中。这意味着:类型参数虽不在运行时“直接可用”,却可通过反射从类结构定义处(如字段、方法、父类继承关系)重新提取出来。关键不在于“阻止擦除”,而在于在擦除发生前,把泛型签名固化到可反射的位置。
以下几种方式是实践中真正有效的保留/恢复泛型信息的方法:
通过字段或方法的 getGenericType() 获取声明时的泛型
泛型信息只要出现在类的结构定义里(比如成员变量、方法返回值、参数),就能被反射读取:
-
Field.getGenericType()→ 返回ParameterizedType,可递归获取嵌套类型 -
Method.getGenericReturnType()/getGenericParameterTypes()→ 同理
例如:
private List<map user>> data;</map>
调用 field.getGenericType() 得到的是 List<map user>></map> 类型树,不是 List。
注意:不能对 new ArrayList() 实例调用 getClass() 去查泛型——实例本身不带泛型元数据,必须从类定义源头入手。
使用 TypeReference(如 Jackson/Gson)捕获完整泛型签名
写法必须带空大括号 {},构造一个匿名子类:
TypeReference<map list>>> ref = new TypeReference<map list>>>() {};</map></map>
原理是:编译器会把这个匿名类的泛型签名写入其 Signature 属性;Jackson 在构造函数中通过 getClass().getGenericSuperclass() 反射读取,还原出完整类型。
这种写法能支持任意深度嵌套(如 Result<page>></page>),且无需手动处理 TypeVariable 或 WildcardType,框架内部已封装好解析逻辑。
显式传入 Class<t></t> 或 Type 对象
适用于泛型类型较简单、单层的情况,比如工厂方法或 JSON 反序列化入口:
public <t> T fromJson(String json, Class<t> clazz) { ... }
fromJson(json, User.class); // OK,但无法表达 List<user></user></t></t>
若需表达带泛型的类型,就得升级为 Type:
public <t> T fromJson(String json, Type type) { ... }
fromJson(json, new TypeToken<list>>(){}.getType());</list></t>
混淆环境下要额外保留 Signature 等属性
使用 ProGuard/R8 时,默认会删掉 Signature、InnerClasses 等元数据,导致上述反射全部失效。必须添加保留规则:
-keepattributes Signature
-keepattributes InnerClasses
-keepattributes EnclosingMethod
-keep class * extends com.google.gson.reflect.TypeToken { <init>(); }</init>
否则即使代码写对了,运行时也拿不到泛型信息。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











