在 Java 中对于下面最简单的泛型类
class A<T> {
public void foo() {
// 如何在此处获得运行时 T 的具体类型呢?
}
}
设想我们使用时
new A<String>().foo();
是否能在
方法中获得当前的类型是 String 呢?答案是否定的,不能。在
- foo()
方法中 this 引用给不出类型信息,
- foo()
就更不可能了,因为 Java 的泛型不等同于 C++ 的模板类,
- this.getClass()
实例例是被所有的不同具体类型的 A 实例 (new A
- this.getClass()
我们可以在 IDE 的调试时看到这个泛型类的签名
或者用
可以查看到类 A 的泛型签名
- javap -v cc.unmi.A
Signature: #17 // <T:Ljava/lang/Object;>Ljava/lang/Object;
为什么说是擦除到上限呢?并不是泛型在字节码中都表示为
, 看下面的例子,假如
- Object
声明如下
- A
class A<T extends Number> {
}
再用
来看泛型签名
- javap -v cc.unmi.A
Signature: #18 // <T:Ljava/lang/Number;>Ljava/lang/Object;
也就是说在上面的
方法中无法获得当前的类型,我们必须给它加个参数 T
- foo()
public void foo(T t) {
t.getClass();
}
了解了 Java 泛型机制是如何擦除类型的,我们接下来的问题就是如何通过反射获得泛型签名中的类型,一般会在继承或实现泛型接口时会用到它。
- package cc.unmi;
- class A<T, ID> {
- }
- class B extends A<String, Integer> {
- }
- public class Generic {
- public static void main(String[] args) {
- System.out.println(B.class.getGenericSuperclass());
- }
- }
上面的代码输出是
本文原始链接 , 来自隔叶黄莺 Unmi Blog
cc.unmi.A<java.lang.String, java.lang.Integer>
所以要获得这两个类型是可行的,设置了断点
这张图可以看到
得到的实际类型是
- B.class.getGenericSuperclass()
通过它就可以获得
- ParameterizedTypeImpl
了。代码就是
- actualTypeArguments
- 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<Ljava/lang/String;Ljava/lang/Integer;>;
这时与继承一个泛型基类的情况略有不同,如下关系,A 是一个泛型接口
- interface A<T, ID> {
- }
- class B implements A<String, Integer> {
- }
该如何反射获得 B 的参数类型呢,用上面的方法已不可行,
已不是一个
- B.class.getGenericSuperclass()
而是一个 Object 类型。现在需要另一个方法
- ParameterizedTypeImpl
它得到一个
- getGenericInterfaces(): Type[]
数组,代表类实现的多个接口,因为我们这儿只实现了一个接口,所以取第一个元素,它的类型是我们已见过的
- Type
,
- ParameterizedTypeImpl
因此我们用来获得实现接口而来的泛型参数的代码就是
- ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type actualTypeArgument : actualTypeArguments) {
- System.out.println(actualTypeArgument);
- }
同样能得到上面的一样的结果。
, 转型为
- getGenericSuperclass()
来获得实际类型
- ParameterizedType
, 针对其中的元素转型为
- getGenericInterfaces()
来获得实际类型
- ParameterizedType
类型,而是擦除到上限类型
- Object
来查看泛型签名来找到线索
- javap -v <your_class>
来源: