java范型运行时类型获取

反射是我喜欢喜欢 java 的一个重要原因,它在保证静态语言特性的同时,为你提供了一些动态的特性,你可以通过反射做一些很令人惊奇的事情。本文将通过反射和继承来让你获取泛型的运行时类型。

java 泛型使用的是 type erasure。它的泛型代码只有一份,泛型实现需要在编译和运行时都进行一定的操作。
它的反射机制也为我们获取运行时泛型类型提供了一些接口:java.lang.reflect.TypeVariablejava.lang.reflect.ParameterizedType

1. 类型变量的声明

TypeVariable类型变量是各种类型变量的通用超接口,主要保存了类型变量(泛型)的具体声明的信息,比如:类型变量的名称,边界等,但是你无法通过该类来获取具体的运行时类型。
它就像是泛型类或者接口的泛型部分的声明而已。而TypeVariable接口只要和另一个接口java.lang.reflect.GenericDeclaration泛型声明紧密关联。

1.1 GenericDeclaration 可以泛型声明的元素

GenericDeclaration是 Java 反射包中,所有可以声明泛型类型的语法元素的父接口。它只有一个方法:

TypeVariable<?>[] getTypeParameters();

这个方法是懒加载泛型声明的所有的类型变量信息。

该接口有三个实现:java.lang.Classjava.lang.reflect.Constructorjava.lang.reflect.Method,也就是说我们可以在类、构造函数、方法上使用泛型变量。
通过调用对应反射的 #getTypeParameters() 方法就可以得到具体的泛型声明信息。

1.2 TypeVariable 泛型声明

接下来让我们看看泛型声明具体都包含了什么信息:

1
2
3
4
5
6
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
Type[] getBounds();
D getGenericDeclaration();
String getName();
AnnotatedType[] getAnnotatedBounds();
}

首先该接口是个泛型接口,里面包含了刚刚的 GenericDeclaration ,即它的信息中包含了具体泛型申明的位置:方法、构造函数还是普通函数,这个信息可以通过 #getGenericDeclaration() 方法获取到。

另外 #getBounds() 可以获取泛型的上限边界信息,#getName() 可以获取泛型声明时的名称,比如我们常用的 <T> 这个 T。

2. 运行时类型的获取

关于运行时类型的获取我们首先要知道一个接口:java.lang.reflect.ParameterizedType 所有泛型运行时的类或者接口都会实现这个接口,将具体的参数类型信息保存在实现中。

1
2
3
4
5
public interface ParameterizedType extends Type { 
Type[] getActualTypeArguments();
Type getRawType();
Type getOwnerType();
}

这里我们就主要关注 getActualTypeArguments() 方法,这个方法就是所有的子类具体泛型参数类型的列表,这个长度和 TypeVariable 中的泛型参数声明列表应该是一样大小的。

java 的泛型是通过 type erasure 实现的,所以普通的静态泛型方法是不能获取到泛型类型的,它在运行时使用的是 cast 来帮助方法的执行。

因为在反射机制中提供了关于继承/实现泛型基类/接口的具体细节,这些细节可以通过 Class#getGenericInfo()获取,注意这是在子/实现类中保存的,我们可以通过 Class 类中的两个具体方法来获取具体的运行时类型:Class<?>#getGenericSuperclass() 获取所有的超类类型;Class<?>#getGenericInterfaces() 获取所有的接口类型。这两个方法可以得到当前类的所有直接超类和接口,然后遍历这些类或者接口如果是 ParameterizedType 类型的,就可以将其转换为 ParameterizedType 然后获取具体的泛型参数类型了。

一定要注意,普通的静态方法是不能获取到具体的参数类型的,因为它在字节码阶段全部都被编译成 java.lang.Object 在运行阶段通过 cast 才转换成你想要的具体类型。


java范型运行时类型获取
https://wttch96.github.io/post/java/java范型运行时类型获取.html
作者
Wttch
发布于
2022年3月29日
许可协议