Solon v3.10.7

泛型嵌套传导分析

</> markdown
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 中,字段 xymn 的实际类型分别是什么?

答案: - xList<String> - yMap<String, Integer> - mString - nInteger

传统反射 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 的泛型传导分析采用逐层追踪策略:

  1. 从子类向上追溯:从具体类开始,逐层查看每一层将泛型变量映射成了什么类型。例如到 B 层发现 X 变成了 List<M>,再到 C 层发现 M 变成了 String
  2. 递归解析嵌套泛型:对于带嵌套的泛型(如 List<M> 中还包含变量 M),继续向内解析,直到得到具体类型。
  3. 结果缓存:解析过的字段类型会被缓存,后续查询同一字段直接返回缓存结果。

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();