Java 运行时如何获取泛型参数的类型
https://blog.csdn.net/hj7jay/article/details/54889717
https://blog.csdn.net/xiaozaq/article/details/52329321
在 Java 中对于下面最简单的泛型类
[java] view plain copy
- class A
{ - public void foo() {
- //如何在此处获得运行时 T 的具体类型呢?
- }
- }
设想我们使用时
[java] view plain copy
- new A
().foo();
是否能在 foo() 方法中获得当前的类型是 String 呢?答案是否定的,不能。在 foo() 方法中 this 引用给不出类型信息, this.getClass() 就更不可能了,因为 Java 的泛型不等同于 C++ 的模板类, this.getClass() 实例例是被所有的不同具体类型的 A 实例(new A
我们可以在 IDE 的调试时看到这个泛型类的签名
或者用 javap -v cc.unmi.A 可以查看到类 A 的泛型签名
Signature: #17 //
为什么说是擦除到上限呢?并不是泛型在字节码中都表示为 Object , 看下面的例子,假如 A 声明如下
class A
}
再用 javap -v cc.unmi.A 来看泛型签名
Signature: #18 //
也就是说在上面的 foo() 方法中无法获得当前的类型,我们必须给它加个参数 T
public void foo(T t) {
t.getClass();
}
了解了 Java 泛型机制是如何擦除类型的,我们接下来的问题就是如何通过反射获得泛型签名中的类型,一般会在继承或实现泛型接口时会用到它。
继承一个泛型基类
[java] view plain copy
- class A
{ - }
- class B extends A
{ - }
- public class Generic {
- public static void main(String[] args) {
- System.out.println(B.class.getGenericSuperclass());
- }
- }
上面的代码输出是
cc.unmi.A
所以要获得这两个类型是可行的,设置了断点
这张图可以看到 B.class.getGenericSuperclass() 得到的实际类型是 ParameterizedTypeImpl 通过它就可以获得 actualTypeArguments 了。代码就是
[java] view plain copy
- ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericSuperclass();
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for(Type actualTypeArgument: actualTypeArguments) {
- System.out.println(actualTypeArgument);
- }
上面的代码输出
class java.lang.String
class java.lang.Integer
我们不妨用 javap -v cc.unmi.B 的泛型签名
Signature: #12 // Lcc/unmi/A
实现一个泛型接口
这时与继承一个泛型基类的情况略有不同,如下关系,A 是一个泛型接口
[java] view plain copy
- interface A
{ - }
- class B implements A
{ - }
该如何反射获得 B 的参数类型呢,用上面的方法已不可行, B.class.getGenericSuperclass() 已不是一个 ParameterizedTypeImpl 而是一个 Object 类型。现在需要另一个方法 getGenericInterfaces(): Type[] 它得到一个 Type 数组,代表类实现的多个接口,因为我们这儿只实现了一个接口,所以取第一个元素,它的类型是我们已见过的 ParameterizedTypeImpl ,
因此我们用来获得实现接口而来的泛型参数的代码就是
[java] view plain copy
- ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type actualTypeArgument : actualTypeArguments) {
- System.out.println(actualTypeArgument);
- }
同样能得到上面的一样的结果。
总结一下
- 如果是继承基类而来的泛型,就用 getGenericSuperclass() , 转型为 ParameterizedType 来获得实际类型
- 如果是实现接口而来的泛型,就用 getGenericInterfaces() , 针对其中的元素转型为 ParameterizedType 来获得实际类型
- 我们所说的 Java 泛型在字节码中会被擦除,并不总是擦除为 Object 类型,而是擦除到上限类型
- 能否获得想要的类型可以在 IDE 中,或用 javap -v
来查看泛型签名来找到线索
example:
获取几种形式泛型的Class类型:
ClassA类:
[java] view plain copy
- import java.lang.reflect.ParameterizedType;
- import java.lang.reflect.Type;
- public class ClassA
{ - private T obj;
- public void setObject(T obj) { this.obj = obj; }
- public T getObject() { return obj; }
- /**
- * 获取T的实际类型
- */
- public void testClassA() throws NoSuchFieldException, SecurityException {
- System.out.print(“getSuperclass:”);
- System.out.println(this.getClass().getSuperclass().getName());
- System.out.print(“getGenericSuperclass:”);
- Type t = this.getClass().getGenericSuperclass();
- System.out.println(t);
- if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
- System.out.print(“getActualTypeArguments:”);
- for (Type t1 : ((ParameterizedType) t).getActualTypeArguments()) {
- System.out.print(t1 + “,”);
- }
- System.out.println();
- }
- }
- }
Test类:
[java] view plain copy
- import java.lang.reflect.Type;
- import java.util.List;
- import java.util.Map;
- import java.lang.reflect.ParameterizedType;
- public class Test extends ClassA
{ - private List
list; - private Map
map; - /***
- * 获取List中的泛型
- */
- public static void testList() throws NoSuchFieldException, SecurityException {
- Type t = Test.class.getDeclaredField(“list”).getGenericType();
- if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
- for (Type t1 : ((ParameterizedType) t).getActualTypeArguments()) {
- System.out.print(t1 + “,”);
- }
- System.out.println();
- }
- }
- /***
- * 获取Map中的泛型
- */
- public static void testMap() throws NoSuchFieldException, SecurityException {
- Type t = Test.class.getDeclaredField(“map”).getGenericType();
- if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
- for (Type t1 : ((ParameterizedType) t).getActualTypeArguments()) {
- System.out.print(t1 + “,”);
- }
- System.out.println();
- }
- }
- public static void main(String args[]) throws Exception {
- System.out.println(“>>>>>>>>>>>testList>>>>>>>>>>>”);
- testList();
- System.out.println(“<<<<<<<<<<<testList<<<<<<<<<<<\n”);
- System.out.println(“>>>>>>>>>>>testMap>>>>>>>>>>>”);
- testMap();
- System.out.println(“<<<<<<<<<<<testMap<<<<<<<<<<<\n”);
- System.out.println(“>>>>>>>>>>>testClassA>>>>>>>>>>>”);
- new Test().testClassA();
- System.out.println(“<<<<<<<<<<<testClassA<<<<<<<<<<<”);
- }
- }
结果:
[plain] view plain copy
testList>>>>>>>>>>>
- class java.lang.String,
- <<<<<<<<<<<testList<<<<<<<<<<<
testMap>>>>>>>>>>>
- class java.lang.String,class java.lang.Object,
- <<<<<<<<<<<testMap<<<<<<<<<<<
testClassA>>>>>>>>>>>
- getSuperclass:com.pelin.util.ClassA
- getGenericSuperclass:com.pelin.util.ClassA
- getActualTypeArguments:class java.lang.String,
- <<<<<<<<<<<testClassA<<<<<<<<<<<
还没有评论,来说两句吧...