原文链接:https://blog.csdn.net/hj7jay/article/details/54889717
在 Java 中对于下面最简单的泛型类
class A<T> {
public void foo() {
//如何在此处获得运行时 T 的具体类型呢?
}
}
设想我们使用时
new A<String>().foo();
是否能在 foo() 方法中获得当前的类型是 String 呢?答案是否定的,不能。在 foo() 方法中 this 引用给不出类型信息, this.getClass() 就更不可能了,因为 Java 的泛型不等同于 C++ 的模板类, this.getClass() 实例例是被所有的不同具体类型的 A 实例(new A<String>(), new A<Integer>() 等) 共享的,所以在字节码中类型会被擦除到上限。
了解了 Java 泛型机制是如何擦除类型的,我们接下来的问题就是如何通过反射获得泛型签名中的类型,一般会在继承或实现泛型接口时会用到它。
继承一个泛型基类
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());
}
}
上面的代码输出是
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
实现一个泛型接口
这时与继承一个泛型基类的情况略有不同,如下关系,A 是一个泛型接口
interface A<T, ID> {
}
class B implements A<String, Integer> {
}
该如何反射获得 B 的参数类型呢,用上面的方法已不可行, B.class.getGenericSuperclass() 已不是一个 ParameterizedTypeImpl 而是一个 Object 类型。现在需要另一个方法 getGenericInterfaces(): Type[] 它得到一个 Type 数组,代表类实现的多个接口,因为我们这儿只实现了一个接口,所以取第一个元素,它的类型是我们已见过的 ParameterizedTypeImpl ,
因此我们用来获得实现接口而来的泛型参数的代码就是
ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
同样能得到上面的一样的结果。
继承一个泛型接口
这时与继承一个泛型基类的情况略有不同,如下关系,A 是一个泛型接口
interface A<T, ID> {
}
interface B extendA<String, Integer> {
}
也可以用
B.class.getGenericInterfaces();
总结一下
如果是继承基类而来的泛型,就用 getGenericSuperclass() , 转型为 ParameterizedType 来获得实际类型
如果是实现接口而来的泛型,就用 getGenericInterfaces() , 针对其中的元素转型为 ParameterizedType 来获得实际类型
我们所说的 Java 泛型在字节码中会被擦除,并不总是擦除为 Object 类型,而是擦除到上限类型
能否获得想要的类型可以在 IDE 中,或用 javap -v <your_class> 来查看泛型签名来找到线索
然后还有一种情况,多重继承
package com.rapid.demo.mybaits;
import cn.hutool.core.util.TypeUtil;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* @Description:
* @Author zbh
* @Date 2023/8/25
*/
public class TypeTest {
interface A<T1, T2> {
}
interface B extends A<String, Integer> {
}
class C implements B{
}
public static void bGetA(){
ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
public static void bGetAEasy(){
for (Type actualTypeArgument :TypeUtil.toParameterizedType(B.class).getActualTypeArguments()){
System.out.println(actualTypeArgument);
}
}
public static void cGetA(){
Type[] genericInterfaces = C.class.getGenericInterfaces();
if (genericInterfaces.length > 0) {
Type genericType = genericInterfaces[0];
ParameterizedType parameterizedType = (ParameterizedType) ((Class)genericType).getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
public static void cGetAEasy(){
for (Type actualTypeArgument :TypeUtil.toParameterizedType(C.class.getGenericInterfaces()[0]).getActualTypeArguments()){
System.out.println(actualTypeArgument);
}
}
public static void main(String[] args) {
bGetA();
cGetA();
bGetAEasy();
cGetAEasy();
}
}
这儿需要再向上找一次!
网友评论