泛型嵌套传导分析
2026年5月29日 上午8:59:26
当泛型通过多层继承传递时,追踪泛型变量的实际类型是一项复杂的工作。EggG 通过 ClassEggg 和 FieldEggg 的配合,可以穿透多层继承链,还原每个字段的完整泛型类型。
1、问题场景
Java 的类型擦除会将泛型信息移除。对于单层泛型,可以通过匿名子类保留信息;但当泛型嵌套在多层继承中时,传统反射 API 无法追踪泛型变量的传导路径。
来看这个三层继承的例子:
// 顶层类:定义泛型占位符 X 和 Y
class A<X, Y> {
public X x;
public Y y;
}
// 中间类:用 List<M> 替换 X,用 Map<String, N> 替换 Y
// 同时引入新的泛型占位符 M 和 N
class B<M, N> extends A<List<M>, Map<String, N>> {
public M m;
public N n;
}
// 具体类:将 M 确定为 String,将 N 确定为 Integer
class C extends B<String, Integer> {
}
问题:在类 C 中,字段 x、y、m、n 的实际类型分别是什么?
答案: - x → List<String> - y → Map<String, Integer> - m → String - n → Integer
传统反射 API 只能获取擦除后的类型(Object),EggG 能够还原这些泛型传导。
2、ClassEggg 概述
ClassEggg 是对 Java Class 的元数据封装,管理字段、方法、构造器和属性。通过 TypeEggg 获取:
Eggg eggg = new Eggg();
ClassEggg classEggg = eggg.getTypeEggg(C.class).getClassEggg();
3、字段泛型追踪
获取所有字段
ClassEggg cw = eggg.getTypeEggg(C.class).getClassEggg();
// 获取所有字段,包括从父类继承的
Collection<FieldEggg> fields = cw.getAllFieldEgggs();
按名称获取字段
FieldEggg xField = cw.getFieldEgggByName("x"); // 从类 A 继承
FieldEggg yField = cw.getFieldEgggByName("y"); // 从类 A 继承
FieldEggg mField = cw.getFieldEgggByName("m"); // 从类 B 继承
FieldEggg nField = cw.getFieldEgggByName("n"); // 从类 B 继承
追踪字段的泛型
// 字段 x:经过传导后,实际类型是 List<String>
FieldEggg xField = cw.getFieldEgggByName("x");
xField.getType(); // List.class
xField.getTypeEggg().isParameterizedType(); // true
xField.getTypeEggg().getActualTypeArguments()[0]; // String.class
// 字段 y:经过传导后,实际类型是 Map<String, Integer>
FieldEggg yField = cw.getFieldEgggByName("y");
yField.getType(); // Map.class
yField.getTypeEggg().isParameterizedType(); // true
yField.getTypeEggg().getActualTypeArguments()[0]; // String.class
yField.getTypeEggg().getActualTypeArguments()[1]; // Integer.class
// 字段 m 和 n:传导后为简单类型
cw.getFieldEgggByName("m").getType(); // String.class
cw.getFieldEgggByName("n").getType(); // Integer.class
4、完整示例
public class GenericAnalysisDemo {
private static final Eggg eggg = new Eggg();
public static void main(String[] args) {
ClassEggg cw = eggg.getTypeEggg(C.class).getClassEggg();
// 遍历所有字段
for (FieldEggg fe : cw.getAllFieldEgggs()) {
Object digest = fe.getDigest();
System.out.println(fe.getName() + " -> " + fe.getType() + " digest=" + digest);
}
// 验证字段 x 的泛型传导
// A<X,Y> 中 X → List<M>,M → String,因此 x 是 List<String>
assert cw.getFieldEgggByName("x").getType() == List.class;
assert cw.getFieldEgggByName("x").getTypeEggg().isParameterizedType();
assert cw.getFieldEgggByName("x").getTypeEggg().getActualTypeArguments()[0]
== String.class;
// 验证字段 y 的泛型传导
// A<X,Y> 中 Y → Map<String,N>,N → Integer,因此 y 是 Map<String, Integer>
assert cw.getFieldEgggByName("y").getType() == Map.class;
assert cw.getFieldEgggByName("y").getTypeEggg().isParameterizedType();
assert cw.getFieldEgggByName("y").getTypeEggg().getActualTypeArguments()[0]
== String.class;
assert cw.getFieldEgggByName("y").getTypeEggg().getActualTypeArguments()[1]
== Integer.class;
// 验证字段 m:B<M,N> 中 M → String
assert cw.getFieldEgggByName("m").getType() == String.class;
// 验证字段 n:B<M,N> 中 N → Integer
assert cw.getFieldEgggByName("n").getType() == Integer.class;
System.out.println("所有断言通过");
}
// 测试用的三层继承结构
public static class A<X, Y> {
public X x;
public Y y;
}
public static class B<M, N> extends A<List<M>, Map<String, N>> {
public M m;
public N n;
}
public static class C extends B<String, Integer> {
}
}
5、设计原理
EggG 的泛型传导分析采用逐层追踪策略:
- 从子类向上追溯:从具体类开始,逐层查看每一层将泛型变量映射成了什么类型。例如到
B层发现X变成了List<M>,再到C层发现M变成了String。 - 递归解析嵌套泛型:对于带嵌套的泛型(如
List<M>中还包含变量M),继续向内解析,直到得到具体类型。 - 结果缓存:解析过的字段类型会被缓存,后续查询同一字段直接返回缓存结果。
6、ClassEggg 更多能力
构造器
// 获取所有构造器
List<ConstrEggg> constructors = cw.getConstrEgggs();
// 获取创造器(带 @Creator 注解的构造器或静态工厂方法)
ConstrEggg creator = cw.getCreator();
// 按参数类型查找构造器
ConstrEggg ctor = cw.findConstrEggg(String.class, int.class);
// 按可用键名匹配构造器(用于反序列化场景)
ConstrEggg matched = cw.matchConstrEggg(availableKeys, defaultCtor);
方法
// 获取公有方法
Collection<MethodEggg> publicMethods = cw.getPublicMethodEgggs();
// 获取声明方法
Collection<MethodEggg> declaredMethods = cw.getDeclaredMethodEgggs();
// 按名称和参数查找方法
MethodEggg method = cw.findMethodEggg("sayHello", String.class);
属性
// 获取所有属性(字段 + getter/setter 的组合)
Collection<PropertyEggg> properties = cw.getPropertyEgggs();
// 按名称获取属性
PropertyEggg prop = cw.getPropertyEgggByName("name");
// 按别名获取属性
PropertyEggg prop = cw.getPropertyEgggByAlias("userName");
Record 支持
// 是否为 Java Record 类
cw.isRealRecordClass();
// 是否疑似 Record(字段全为 final,有全参构造器)
cw.isLikeRecordClass();