java泛型运行时虽被擦除,但签名存于class文件signature属性中;jackson通过反射字段/方法的getgenerictype或typereference匿名子类获取完整泛型类型树,支持多层嵌套与typevariable/wildcardtype处理。

Java泛型在运行时确实被擦除,但编译器会把完整的泛型签名(如 Map<string list>></string>)写入 class 文件的 Signature 属性中——这个信息不会丢,只要反射路径正确,Jackson 就能层层解析出来。
从字段或方法签名入手,不是从对象实例
关键前提:泛型类型必须在类结构中“声明过”,比如作为成员变量、方法返回值或参数。不能指望对一个 new ArrayList() 实例调用 getClass() 来获取 User ——那只会得到 ArrayList.class。
- 若字段定义为
private Map<string list>> userMap;</string>,可通过field.getGenericType()拿到ParameterizedType - 若方法声明为
public ResponseData<list>> fetchOrders()</list>,则用method.getGenericReturnType()提取 - 提取后需递归处理嵌套:先拿到
ResponseData<...></...>的实际类型参数,再对List<order></order>继续拆解
TypeReference 是封装好的“签名捕获器”
写 new TypeReference<map list>>>() {}</map> 的本质,是让编译器生成一个匿名子类,并把泛型签名固化进该子类的 Signature 属性里。Jackson 在构造函数中通过 getClass().getGenericSuperclass() 反射读取这个签名,从而还原出完整类型树。
- 它不依赖字段或方法,而是靠“继承关系+匿名类”把泛型锁死在类定义层面
- 即使类型嵌套三层(如
Result<pageinfo>></pageinfo>),也能一次性捕获全部层级 - 注意写法必须带空大括号
{},否则无法生成子类,签名就取不到
手动解析嵌套泛型时要处理 TypeVariable 和通配符
并非所有泛型参数都是具体类。遇到 T extends BaseModel 或 ? super String 时,getActualTypeArguments() 返回的可能是 TypeVariable 或 WildcardType,直接强转 Class 会抛异常。
- 对
TypeVariable,可尝试通过所在类的泛型声明上下文推导边界类型(例如查BaseService<t extends basemodel></t>中的T边界) - 对
WildcardType,需检查getUpperBounds()和getLowerBounds(),通常上界是Object,下界才具业务意义 - Jackson 内部的
JavaType系统已封装这些逻辑,所以优先用TypeReference而非裸反射
实战建议:复杂嵌套优先走 TypeReference,动态场景再考虑字段/方法反射
如果你控制反序列化入口(如统一 JSON 响应处理器),直接传 TypeReference 最稳;如果是在通用 DAO 层自动适配返回类型(比如 MyBatis-Plus 风格),那就得扫描方法返回签名,结合 ParameterizedType 逐层解析。
- 避免在运行时拼接字符串构造泛型类型,不可靠且难调试
- 用 Jackson 的
objectMapper.getTypeFactory().constructType(...)可将Type转为内部JavaType,便于后续复用 - 日志中打印
javaType.toCanonical()能直观看到 Jackson 解析出的最终类型表达式










