Java如何获得泛型类中的泛型类型T.class
作者:nvd11
Java获得泛型类的泛型类型T.class
假如我们写了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()); } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。