Java 获得泛型类中的泛型类型 T.class

本文探讨了如何在Java中获取泛型类的泛型类型,包括使用反射和构造方法的两种方案。通过实例分析,解释了为何直接使用`E.class`或`E.getClass()`无法获取到泛型的Class对象,并详细展示了如何通过反射从匿名子类或具体子类中获取泛型类型。同时,提出了一种构造方法的解决方案,即在初始化时传入泛型的Class对象,但这种方法可能增加出错风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

假如我们写了1个泛型类MyBase

public class MyBase<E> {
    public MyBase(){
        
    }

    public Class<?> getEClass() {}   
}

我想写1个getEClass 的方法, 获取泛型符号E的Class对象。
直接用E.class E.getClass()都是不合法的, 因为E只是个符号, 没有被实例化。

1. 反射方案

各种google 各种Stackoverflow 后, 找到利用反射的1个方法。

public class MyBase<E> {
    public MyBase(){
        
    }

    public Class<?> getEClass() {
        
        //get the Class object of this own class 
        Class<? extends MyBase> thisClass = this.getClass();

        //get the Type Object of supper class
        Type superClassType = thisClass.getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType)superClassType;

        //get the Generic Type array
        Type[] genTypeArr = pt.getActualTypeArguments();
        Type genType = genTypeArr[0];
        if (false == genType instanceof Class){
            return Object.class;
        }

        return (Class<Object>) genType;
    }
}

这个方法被测试过, 但是有个很特别的限制,
一定要实例化匿名子类才有效果。

例如

   @Test
    void testMyBase() {
        MyBase<String> mybase = new MyBase<String>(){};
        log.info("E class is {}",mybase.getEClass());
    }

注意new MyBase(){} 后面的大括号, 意思不是实例化MyBase对象, 而是实例化MyBase的匿名子类。

如果我们写多1个具体的子类MyList

public class MyList<E> extends MyBase<E> {
    public MyList(){

	}
}

测试方法

   @Test
    void testGetEClass() {
        MyList<String> mylist = new MyList<String>();
        log.info("E class is {}",mylist.getEClass());
    }

同样不行, 原因是

		//get the Type Object of supper class
        Type superClassType = thisClass.getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType)superClassType;

这里 我们只能通过本类的Class对象去获得1个父类 ParameterizedType (包括泛型参数列表的Type)

而JDK本身只提供了getGenericSuperclass()方法去获得父类的Type 对象
但是没有提供例如getGenericClass() 去获取本类的Type对象…

所以一旦我们不实用匿名子类
当下面代码被执行时

        MyList<String> mylist = new MyList<String>();
        log.info("E class is {}",mylist.getEClass());

thisClass.getGenericSuperclass() 会获得MyList的父类MyBase的Type, 问题是MyBase没有被实例化, 所以pt.getActualTypeArguments()[0] 只会返回E
本身 就return null了

但是如果我们利用匿名子类的化
当下面代码被执行时

        MyList<String> mylist = new MyList<String>(){};
        log.info("E class is {}",mylist.getEClass());

mylist 的类实际上是1个匿名子类MyList$1, 它的父类就是MyList, 而匿名子类的父类在JVM实际上是被实例化的,所以
所以pt.getActualTypeArguments()[0] 会返回泛型具体类String 的Class。



2. 反射方案2

匿名子类毕竟有点奇怪。
如果避免使用你匿名子类, 我们还有1个办法

public class MySet extends MyBase<String> {
    public MySet(){
  
    }
}

如上面这个MySet类, 它直接继承MyBase时指定了具体泛型, 令到自己本身不是1个泛型类。

这样的话, 不用匿名子类也一样可以获得泛型具体类型。
因为这种情况下, 一旦实现MySet, 因为MySet指定了继承泛型的具体类型, 在JVM 中MyBase也会被实例化。

但是这个方案一样不好, 不灵活是其次, 我都会指定了具体泛型类型了, 何必用再用getEClass()去获得泛型具体类型? 多次一举

  @Test
    void testGetEClass2() {
        MySet myset = new MySet();
        log.info("Set E class is {}",myset.getEClass());
    }



3. 构造方法方案

这也是外网强烈推荐的方案

public class MyMap<E> {

    private Class<E> eClass;

    public MyMap(Class<E> eClass){
        this.eClass = eClass;
    }

    public Class<E> getEClass(){
        return this.eClass;
    }

}

让开发人员在初始化时,顺便外部传入泛型的类, 不过手抖写错的风险…

测试方法:

  @Test
    void testGetEClass3() {
        MyMap<String> myMap= new MyMap<>(String.class);
        log.info("Set E class is {}",myMap.getEClass());
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nvd11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值