泛型嵌套的关键是编写时保持类型可推导、解析时绕过类型擦除;Java编译期保留泛型签名但运行时擦除,需通过getGenericXxx()反射接口递归解析Type树,推荐使用Spring ResolvableType或Guava TypeToken简化处理。

泛型嵌套不是“多写几层尖括号”那么简单,关键在**编写时保持类型可推导、解析时绕过类型擦除**。Java 编译期保留泛型签名,但运行时只留原始类型——所以写法要配合反射机制,才能真正拿到 List<map list>>></map> 这样的完整结构。
编写:让嵌套有层次、可绑定
定义嵌套泛型类时,重点是明确每层的职责和上下文依赖:
- 用多参数泛型表达语义层级,比如
PermissionNode<subject resource action context></subject>,比Node<object></object>更具约束力 - 避免裸通配符,优先用带界限定的写法,如
Permission extends Identifiable, ? extends ActionEnum>,防止类型失控 - 内部类使用泛型时,必须显式传入
ownerType(所属外层实例的类型),否则getGenericType()无法正确绑定外层类型变量 - 数组嵌套要区分
T[]和List<t>[]</t>:后者属于GenericArrayType,需调用getGenericComponentType()再递归解析
解析:靠 getGenericXxx() + 递归展开 Type 树
不能用 getClass(),它只返回擦除后的 ArrayList.class;必须用反射接口获取编译期保留的泛型结构:
- 字段类型:用
field.getGenericType()→ 返回Type,可能是ParameterizedType、TypeVariable或WildcardType - 方法参数:用
method.getGenericParameterTypes();返回值用method.getGenericReturnType() - 遇到
ParameterizedType就拆:调getRawType()拿原始类(如Map.class),再调getActualTypeArguments()拿参数数组(如[String.class, ParameterizedType]) - 对每个实参继续判断类型:是
Class就终止;是另一个ParameterizedType就递归;是TypeVariable就查上下文(比如父类Box<string></string>中的T绑定为String)
推荐:别手写全套解析逻辑
手动处理 GenericArrayType、WildcardType、多层继承中的 TypeVariable 映射,容易出错且维护成本高:
-
Spring ResolvableType:一行搞定,自动处理字段/方法/父类泛型绑定,例如
ResolvableType.forField(field)直接返回完整嵌套视图 -
Guava TypeToken:适合从字面量构造,如
TypeToken<list integer>>>.getType()</list>,能保留全部嵌套信息用于反序列化 - Fastjson / Jackson 解析多层嵌套时,也得传
TypeReference或JavaType,本质就是把 Type 树交给它们去走一遍上述逻辑
实战小提醒
写嵌套泛型不是炫技,而是为了在关键路径上守住类型边界。比如权限系统里,PermissionNode<tenantadmin product update maskingcontext></tenantadmin> 一旦实例化,四个类型就全闭合,后续校验、脱敏、日志都能精准消费,不会因擦除变成模糊的 Object。


















