You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java如何获取对象的第二个泛型类型?

嘿,这个问题的核心其实是Java的类型擦除在搞鬼!你现在拿第一个泛型参数没问题,是因为Decision继承Concept时把第一个参数固定成了String(具体类型),但第二个参数S是Decision自己的泛型变量,运行时会被擦除,直接用原来的反射逻辑拿的话,得到的是一个TypeVariable而不是具体的Class,强转肯定报错。

给你两种解决思路,按需选择:

方案1:用匿名内部类保留泛型信息(快速测试用)

如果你只是想快速验证效果,可以用匿名内部类的方式实例化Decision——JVM会给匿名内部类保留泛型参数的元数据:

public class Main {
    public static void main(String[] args){
        // 注意后面的大括号,这是创建匿名内部类,不是直接实例化Decision
        Decision<Boolean> myBooleanDecision = new Decision<Boolean>() {};
    }
}

然后修改Concept类,新增一个获取第二个参数的方法:

public final Class<?> getSecondTypeParam() {
    ParameterizedType superType = (ParameterizedType) getClass().getGenericSuperclass();
    Type secondArg = superType.getActualTypeArguments()[1];
    
    // 如果当前类是匿名内部类,这里的secondArg是TypeVariable(代表Decision的S)
    if (secondArg instanceof TypeVariable) {
        // 再去拿Decision的泛型实际参数
        ParameterizedType decisionSuperType = (ParameterizedType) getClass().getGenericSuperclass();
        Type actualType = decisionSuperType.getActualTypeArguments()[0];
        return (Class<?>) actualType;
    }
    // 如果是其他场景(比如子类明确指定了S的具体类型)
    return (Class<?>) secondArg;
}

然后在Decision的构造里调用getSecondTypeParam(),就能输出class java.lang.Boolean了。

方案2:构造传参(生产环境更靠谱)

反射的方式太依赖泛元数据是否被保留,很多复杂场景下会失效。更稳妥的方式是在创建实例时主动把类型传进去:

abstract class Concept<T, S> {
    private final Class<S> secondType;

    // 父类构造接收第二个泛型的Class
    public Concept(Class<S> secondType) {
        this.secondType = secondType;
    }

    public final Class<?> getTypeParam() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<?>) parameterizedType.getActualTypeArguments()[0];
    }

    public final Class<S> getSecondTypeParam() {
        return secondType;
    }
}

public class Decision<S> extends Concept<String, S> {
    // 子类构造把类型传给父类
    public Decision(Class<S> secondType) {
        super(secondType);
        System.out.println(getSecondTypeParam());
    }
}

// 测试时传入Boolean.class
public class Main {
    public static void main(String[] args){
        Decision<Boolean> myBooleanDecision = new Decision<>(Boolean.class);
    }
}

这种方式完全不依赖反射,不管什么场景都能稳定拿到类型,生产环境更推荐用这个。

为啥第一个参数能拿到?

顺便说下,你原来的方法能拿到String,是因为Decision继承Concept时把第一个泛型参数硬编码成了String——这是个具体类型,不是泛型变量,所以反射能直接拿到它的Class对象。而第二个参数是S,属于Decision的泛型参数,运行时默认会被擦除成Object,只有当你明确用子类或匿名内部类把S绑定到具体类型时,反射才能抓到这个信息。

内容的提问来源于stack exchange,提问作者Edwin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 16:12:41