java泛型擦除不可逆,所谓“类型恢复”实为通过typereference/typetoken等显式传入泛型签名,主流json库均依赖匿名子类机制提取parameterizedtype,辅以具体子类封装或字符串+类型标识组合方案绕过限制。

Java 反序列化时无法自动恢复泛型类型,因为擦除是编译期不可逆行为——运行时 List<user></user> 就是 List,JVM 不保留 User 这个信息。所谓“类型恢复”,不是还原擦除,而是通过外部手段把缺失的泛型签名补回来,让反序列化器知道该构造什么具体类型。
用 TypeReference 或 TypeToken 显式传入完整泛型结构
这是最常用、最可靠的方案,适用于 Jackson、Gson、Fastjson2 等主流 JSON 库:
- Jackson:调用
objectMapper.readValue(json, new TypeReference<list>>() {})</list>;内部利用匿名子类的getGenericSuperclass()提取List<user></user>的ParameterizedType - Gson:写成
gson.fromJson(json, new TypeToken<list>>() {}.getType())</list>;TypeToken同样靠匿名子类固化类型信息 - 注意:不能写
new TypeReference<list>>().getClass()</list>这种直接实例化,必须是匿名子类(带{}),否则泛型信息在字节码中不保留
为泛型字段定义具体子类或封装类
当结构固定、复用频繁时,可避免每次写匿名类:
- 比如响应体是
Result<list>></list>,就定义一个具体类:public class OrderResult extends Result<list>> {}</list> - 反序列化时直接传
OrderResult.class,框架能通过父类声明获取完整泛型参数 - 对 DTO 中的泛型字段(如
private List<item> items;</item>),确保字段声明明确,不要用List items;这种原始类型——反射才能从getGenericType()拿到ParameterizedType
绕过泛型,用字符串+类型标识组合传输
适合跨服务、需强兼容或泛型嵌套很深的场景:
- 不把
Result<t></t>当作整体序列化,而是拆成两部分:String dataJson+String dataType(如"com.example.User") - 发送前手动序列化
T实例为 JSON 字符串,并记录其实际类型名 - 接收端根据
dataType加载 Class,再用 Gson/Jackson 的fromJson(jsonStr, clazz)解析,完全避开泛型擦除路径
警惕 Hessian2/Kryo 等二进制序列化器的默认行为
它们比 JSON 库更依赖运行时类型推断,擦除影响更直接:
- Hessian2 默认不写泛型信息,Dubbo 需显式开启:
dubbo.protocol.hessian2.generic=true - Kryo 推荐注册具体类型(如
kryo.register(ArrayList.class, new ArrayListSerializer())),或改用ImmutableList<user></user>这类已知类型的不可变集合 - 避免用
List>或Map, ?>作为字段类型——通配符会让序列化器彻底放弃类型推导
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











