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
相关产品推荐
相关产品推荐

