反射二:泛型相关周边信息获取

相关文章:
Java Relfect
Java RelfectUtils
反射一:基本类周边信息获取
反射二:泛型相关周边信息获取
反射三:类内部信息获取

在上篇中,讲解了如何利用反射来获取普通类型的类的使用,今天给大家讲解下,有关如何使用反射来获取泛型中的信息。

一、获取泛型超类和接口的相关信息


在这部分内容中,我们将讲述如何获取泛型的超类和接口,把上篇中遗留下来的两个函数先讲完。

1、获取泛型超类相关信息

上篇中,我们讲了,要获取泛型类型的超类,要用到一个函数:

1
2
// 针对泛型父类而设计
public Type getGenericSuperclass();

下面我们就先看看这个函数怎么用,我们依然以上篇中的 Point 类以及它的派生类 PointImpl 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Point 泛型类的实现
public class Point<T> {
private T x,y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
// PointImpl 类的实现
public class PointImpl extends Point<Integer> {
}

从上面的代码中,我们可以看到,Point 类是一个泛型类,具有一个泛型变量 T;而 PointImpl 派生自 Point 并且在派生时,将 Point 进行填充为 Point<Integer>,即将 Point 中的泛型变量填充为 Integer 类型。

下面, 我们将通过反射获取 PointImpl 的父类的类型,以及 PointImpl 的填充类型。我们在没看代码之前,我们先看看结果,我们知道 PointImpl 的父类类型是 Point,而 PointImpl 的填充类型应该是 Integer。然后我们再看看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Class<?> clazz = PointImpl.class;
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type parameterArgType : actualTypeArguments) {
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG,"填充类型为:" + parameterArgClass.getName());
}
// 返回 Type 对象,表示声明此类型的类或接口。
Type type1 = parameterizedType.getRawType();
Class class22 = (Class) type1;
Log.d(TAG, "PointImpl 的父类类型为:" + class22.getName());
}

结果如下:

从结果中,我们可以看到,先获得到的是 PointImpl 在填充父类时的类型 Integer,然后获得的是 PointImpl 的父类类型。下面先看如何获取当前类在填充父类时的填充类型的。对应代码是这一块:

1
2
3
4
5
6
7
8
9
10
11
Class<?> clazz = PointImpl.class;
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type parameterArgType : actualTypeArguments) {
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG, "填充类型为:" + parameterArgClass.getName());
}
}

1.1、获取泛型超类
1
2
Class<?> clazz = PointImpl.class;
Type type = clazz.getGenericSuperclass();

在这段代码中,我们通过 clazz.getGenericSuperclass() 获取 PointImpl.class 的超类。由于我们知道 PointImpl.class 的父类是泛型,所以我们只能使用 clazz.getGenericSuperclass() 来获取。得到的 type 的值对应的是 Point,而 type 对应的是 ParameterizedType,所以我们用 type instanceof ParameterizedType 来识别,然后将 type 变量强转为 ParameterizedType变量;再然后利用 parameterizedType.getActualTypeArguments() 获取当前泛型变量的填充列表,我们知道 Point 中泛型变量 T 被填充为Integer,所以我们得到的数组 Type[] 里,只有一个值,它对应的就是 Integer.Class;
最后我们将得到的 Type 进行强转成 Class 类型,所以 parameterArgClass 对应的值就是 Integer.Class。所以parameterArgClass.getName() 的值为:java.lang.Integer。

上述所说的 Type 是什么呢?

1.2、Type 类型

我们先看看 Type 的源码,看他自己是怎么说的:

1
2
3
4
5
6
7
8
9
package java.lang.reflect;
/**
* Common interface implemented by all Java types.
* @since 1.5
*/
public interface Type {
// Empty
}

Type 是一个接口,这里意思是它是 Java 所有类型都会继承这个接口。但通过源码会发现 String、Integer、Double 这些类都没有继承这个接口,就连 Object 也没继承。再仔细查代码会出现,Class 继承了这个接口:

1
2
3
public final class Class<T> implements Serializable, AnnotatedElement, GenericDeclaration, Type {
...
}

所以说,这个 Type 类型是泛型所特有的。那它用是来做什么的呢?他就是用来标识,当前 Class 中所填充的类型的。意思是,当我们在填充一个泛型时,比如上面我们的 PointImpl extends Point,这个填充类型就会放在 Type 的保存起来,当需要用到的时候再取出来。那问题又来了,我们这里填充的是 Integer 类型,那如果我们填充的是数组泛型呢,比如 Point,再假如我们填充的是一个通配符呢?这Type要怎么识别呢?

为了解决这个问题,Java 的开发者,在 Type 的基础上派生了另外几个接口,分别来保存不同的类型,他们分别是:

  • ParameterizedType
    这就是上面我们代码中用到的,他代表的是一个泛型类型,比如 Point,它就是一个泛型类型。
  • TypeVariable
    这个代表的就是泛型变量,例如 Point<T>,这里面的 T 就是泛型变量,而如果我们利用一种方法获得的对象是 T,那它对应的类型就是 TypeVariable。这个类型的应用后面会细讲。
  • WildcardType
    上面的 TypeVariable 对应的是泛型变量,而如果我们得到不是泛型变量,而是通配符比如:? extends Integer,那它对应的类型就是WildcardType。
  • GenericArrayType
    如果我们得到的是类似 String[] 这种数组形式的表达式,那它对应的类型就是 GenericArrayType,非常值得注意的是如果 type 对应的是表达式是 ArrayList 这种的,这个 type 类型应该是 ParameterizedType,而不是 GenericArrayType,只有类似 Integer[] 这种的才是 GenericArrayType 类型。

虽然我们后面会对 TypeVariable、WildcardType 进行讲解,这里还是先对他们三个类型对应的意义先总结一下,比如我们这里的 clazz.getGenericSuperclass(),得到的 Type 对应的是完整的泛型表达式即:Point,那它对应的类型就是 ParameterizedType,如果我们得到的 Type 对应的表达式,仅仅是 Point 中用来填充泛型变量 T 的 Integer,那这个 Type 对应的类型就是 TypeVariable,如果我们得到的是依然是填充泛型变量 T 的填充类型,这而个填充类型却是通配符 ?,那这个 Type 对应的类型就是 WildcardType。这一段看不大明白也没关系,后面还会再讲。

1.3、ParameterizedType

上面我们已经提到当获取的 Type 类型,对应的是一个完整泛型表达式的时候(比如,我们这里获取到的 PointImpl.class 的父类)type 对应的完整表达式就是 Point。

在 ParameterizedType 中有两个极有用的函数:

1
2
Type[] getActualTypeArguments();
Type getRawType();

  • getActualTypeArguments()
    用来返回当前泛型表达式中,用来填充泛型变量的真正值的列表。像我们这里得到的 Point,用来填充泛型变量 T 的是 Integer 类型,所以这里返回的 Integer 类型所对应的 Class 对象。
  • getRawType()
    上面示例这个它返回的值是 com.xxt.xtest.Point,它的意义是声明当前泛型表达式的类或者接口的 Class 对象。比如,我们这里的 type 对应的是 Point,而声明 Point 这个泛型的当然是 Point 类型,所以返回的是 Point.Class。

下面我们再回过来看看 getActualTypeArguments()。

我们上面说到,这个函数将返回用来填充泛型变量真实参数列表。像我们这里的是 Point,将返回 Integer 对应的 Class 对象。而并不是所有的每次都会返回填充类型对应的 Class 对象。我们知道我们在填充一个泛型时,是存在各种可能的,比如 Point、Point<? extends Number>、Point、Point> 等等。虽然我们没办法穷举可能填充为哪些类型,但我们知道 Type 类型是用来表示填充泛型变量的类型的,而继承 Type 接口只有五个:Class、ParameterizedType、TypeVariable、WildcardType、GenericArrayType。所以这也是 Type[] getActualTypeArguments(); 中 Type[] 数组的所有可能取值。

1.4、getRawType()

来看看 getRawType的用法:

1
2
3
Type type1 = parameterizedType.getRawType();
Class class22 = (Class) type1;
Log.d(TAG, "PointImpl的父类类型为:" + class22.getName());

我们知道,parameterizedType 对应的值是 Point,而 parameterizedType.getRawType() 得到的就是声明这个泛型的类的Class对象。所以这里的 type1 对应的值就是 Point.Class。将其转换成 Class 对象,通过 class22.getName() 得到的值是:com.xxt.xtest.Point。

2、获取泛型接口相关信息

上泛我们也说到,获取普通类所继承的接口使用的是 Class.getInterfaces() 函数,如果要获取泛型接口的对象需要用到:

1
public Type[] getGenericInterfaces();

注意:getGenericInterfaces() 数与 getInterfaces() 函数一样,都只能获取此类直接继承的接口列表。

这里得到的一个 Type 数组,因为我们一个类可以继承多个接口,所以这里的每一个 type 对应的就是我们所继承的一个接口类型。

下面我们举个例子来看这个接口的用法。首先,生成一个泛型接口:

1
2
public interface PointInterface<T,U> {
}

可以看到,我们这个泛型接口里有两个泛型变量,这个接口里我们没有定义任何的方法,因为我们这里只会获取填充泛型接口的实际类型,不会用到它的方法,所以就没有必要生成了,写个空接口即可。

然后,我们直接使用前面的 PointImpl 来继承好了,就不再另写其它类了:

1
2
public class PointImpl extends Point<Integer> implements PointInterface<String,Double> {
}

从这里可以看出,我们在生成 PointImpl 时将 PointInterface<T, U> 填充为 PointInterface<String, Double>。下面我们来看如何来获取 PointImpl 所继承的泛型接口的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Class<?> clazz = PointImpl.class;
Type[] types = clazz.getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type parameterArgType : actualTypeArguments) {
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName());
}
// 返回 Type 对象,表示声明此类型的类或接口。
Type type1 = parameterizedType.getRawType();
Class class22 = (Class) type1;
Log.d(TAG,"声明此接口的类型为:"+class22.getName());
}
}

程序运行结果如下:

代码分析。首先,是获得 PointImpl.class 所继承接口的数组:

1
2
Class<?> clazz = PointImpl.class;
Type[] types = clazz.getGenericInterfaces();

因为我们知道,我们的 PointImpl 只继承了一个接口:PointInterface<String, Double>,所以此时的 Type[] 中只有一个元素,即代表着 PointInterface<String, Double> 的 type。然后是利用 for…each 循环遍历 types 中的每一个元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type parameterArgType : actualTypeArguments) {
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName());
}
// 返回 Type 对象,表示声明此类型的类或接口。
Type type1 = parameterizedType.getRawType();
Class class22 = (Class) type1;
Log.d(TAG, "声明此接口的类型为:"+class22.getName());
}

因为我们知道,我们这里的 type 代表的是 PointInterface<String, Double>,明显它是一个泛型,所以它对应的 type 类型应该是 ParameterizedType。下面的代码就与上面获取泛型超类的一样了,即通过 parameterizedType.getActualTypeArguments() 获取到它的参数数组:

1
2
3
4
5
6
7
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type parameterArgType : actualTypeArguments) {
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName());
}

因为我们知道,PointInterface<T, U> 被 PointImpl 填充为 PointInterface<String,Double>,所以它的真实的参数类型应该是 String 和 Double。

我们前面说过 Type 只有五种类型:Class、ParameterizedType、TypeVariable、WildcardType、GenericArrayType。而 ParameterizedType 代表完整的泛型表达式,TypeVariable 代表泛型变量的符号即 T,U 等,WildcardType 代表通配符,GenericArrayType 代表数组类型,而 Class 则表示派生于 Object 的所有 Class 类,明显这里的 String 和 Double 是Class类型的。所以我们将它们强转为 Class 类型,然后通过 parameterArgClass.getName() 得到它们的完整路径名。最后通过 parameterizedType.getRawType() 获取声明 PointInterface<String,Double> 的接口类类型,虽然这里得到的是 Type,但我们声明接口的是 PointInterface.Class。所以,也是 Class 类型,直接将其强转为 Class 即可。最后通过 Class.getName() 获取其完整的路径名。

1
2
3
4
// 返回 Type 对象,表示声明此类型的类或接口。
Type type1 = parameterizedType.getRawType();
Class class22 = (Class) type1;
Log.d(TAG, "声明此接口的类型为:"+class22.getName());

好了,到这里,有关泛型超类和继承接口的信息获取到这就结束了,下面我们再来看看上面另外提到的另外三个 Type 类型:TypeVariable、WildcardType、GenericArrayType。

二、Type 的五种类型


上面说们说过,Type 接口是用来保存当前泛型被填充的类型的,它总共有五种类型:Class、ParameterizedType、TypeVariable、WildcardType、GenericArrayType。

在这上面的例子中,我们用到了 Class、ParameterizedType。

  • 当 type 所代表的表达式是一个完整泛型时,比如 Point,那这个 Type 类型就是 ParameterizedType;
  • 如果 type 所代表的是一个确定的类,比如 Integer、String、Double 等,那这个 type 所对应的类型就是 Class,强转之后,得到的就是他们所对应的 Class 对象,即 Integer.Class、String.Class、Double.Class 等;
  • 如果 type 对应的是一个泛型变量,即类似于 T 或 U 这种还没有被填充的泛型变量,那它的类型就是 TypeVariable;
  • 而如果 type 对应的是一个通配符表达式,比如 ? extends Num,或者仅仅是一个通配符 ?,类似这种有通符符的类型就是 WildcardType;
  • 而如果 type 对应的类型是类似于 String[] 的数组,那它的类型就是GenericArrayType。

下面我们就来分别看看 TypeVariable、WildcardType 和 GenericArrayType 的用法。

1、TypeVariable

我们上面说了,当 type 代表的类型是一个泛型变量时,它的类型就是 TypeVariable。TypeVariable 有两个函数:

1
2
String getName();
Type[] getBounds();

  • getName:就是得到当前泛型变量的名称;
  • getBounds:返回表示此类型变量上边界的 Type 对象的数组。如果没有上边界,则默认返回 Object。

有关这两个函数我们举个例子来详细说明,我们依然在 PointInterface 泛型接口上做文章:

1
2
3
4
5
public interface PointInterface<T,U> {
}
public class PointGenericityImpl<T extends Number& Serializable>
implements PointInterface<T,Integer> {
}

这里,我们在 PointInterface 的基础上,重写一个类 PointGenericityImpl,与上面直接在类中填充不同的是,它是一个泛型类。首先,将 PointInterface<T, U> 填充为 PointInterface<T, Integer>,即第一个参数依然是一个泛型,而第二个参数填充为 Integer;而我们也给 PointGenericityImpl 中的泛型变量 T 添加了限定:T extends Number&Serializable,给它添加了extends 限定(上边界),指定 T 必须派生自 Number 类和 Serializable 类。

我们再看一下如何获取信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Class<?> clazz = PointGenericityImpl.class;
Type[] types = clazz.getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type parameterArgType : actualTypeArguments) {
if(parameterArgType instanceof TypeVariable){
TypeVariable typeVariable = (TypeVariable) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + typeVariable.getName());
// 返回表示此类型变量上边界的 Type 对象的数组。
Type[] typeBounds = typeVariable.getBounds();
for (Type bound : typeBounds){
Class<?> boundClass = (Class)bound;
// 如果不写,则默认输出 Object,如果写了,则输出对应的
Log.d(TAG, "bound为:" + boundClass.getName());
}
}
if (parameterArgType instanceof Class){
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName());
}
}
}
}

先看看结果:

我们逐段来分析下;首先,获取 PointGenericityImpl 直接继承的的泛型接口数组:

1
2
Class<?> clazz = PointGenericityImpl.class;
Type[] types = clazz.getGenericInterfaces();

我们知道 PointGenericityImpl 只直接继承了一个接口:PointInterface<T, Integer>,其中 T 的限定为:<T extends Number&Serializable>;所以 types 中只有一个元素,这个 type 元素代表的是 PointInterface<T, Integer>,明显它是一个泛型,所以这个 type 的类型是 ParameterizedType。所以,我们下面虽然用了 for …each 进行了列举了 types 中的所有元素,但我们知道它只有一个元素。然后我们将这个元素强转为 ParameterizedType,利用 parameterizedType.getActualTypeArguments() 得到 PointInterface<T,U> 中 T 和 U 被填充的真实类型对应的 Type 数组。

1
2
3
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

我们知道,PointInterface<T,U> 被真实填充为了 PointInterface<T,Integer>,其中 T 的限定为:<T extends Number&Serializable>;所以这个 Type[] 数组包含两个变量,一个是 T,一个是 Integer。我们知道 T 是一个泛型变量,所以对应的类型应该是 TypeVariable。而 Integer 则是一个具体的类,它对应的类型应该是 Class。

  • 针对 T :
1
2
3
4
5
6
7
8
9
10
11
12
if(parameterArgType instanceof TypeVariable){
TypeVariable typeVariable = (TypeVariable) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + typeVariable.getName());
// 返回表示此类型变量上边界的 Type 对象的数组。
Type[] typeBounds = typeVariable.getBounds();
for (Type bound : typeBounds){
Class<?> boundClass = (Class)bound;
// 如果不写,则默认输出 Object,如果写了,则输出对应的
Log.d(TAG, "bound为:" + boundClass.getName());
}
}

我们知道 T 对应的 type 是 TypeVariable,所以将它强转为 TypeVariable 变量。然后用 typeVariable.getName() 获取这个填充的泛型变量的名字,得到的值为:T。

然后,利用 typeVariable.getBounds() 得到 T 的限定条件:上边界的数组。上边界的意思就是 extends 关键字后面的限定条件。“上”的意思就是能取到的最大父类。最大父类当然是用 extends 关键字来限定的。我们知道这里的 T 的限定条件是:<T extends Number&Serializable>,所以 Type[] typeBounds = typeVariable.getBounds(); 所得到 typeBounds 有两个变量,一个是Number,一个是 Serializable。这两个都是具体的类型,所以我们可以直接将它们转换为 Class 类型,然后利用 Class.getName() 获取它们完整的路径名,结果如下:(有关上下边界的意义下面在讲WildcardType时会有图文讲解)

  • 针对 Integer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Class<?> clazz = PointGenericityImpl.class;
Type[] types = clazz.getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type parameterArgType : actualTypeArguments) {
...
if (parameterArgType instanceof Class) {
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName());
}
}
}
}

第一个类型是 type 类型对应的是 T,第二个 type 类型则是 Integer 类型。明显 Integer 是一个 Class 类型,所以我们直接将它强转为 Class 即可。

1
2
3
4
if (parameterArgType instanceof Class){
Class parameterArgClass = (Class) parameterArgType;
Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName());
}

2、GenericArrayType

上面我们说过,当 type 对应的类型是类似于 String[]、Integer[] 等的数组时,那 type 的类型就是 GenericArrayType。这里要特别说明的如果 type 对应的是类似于 ArrayList、List 这样的类型,那 type 的类型应该是 ParameterizedType,而不是 GenericArrayType,因为 ArrayList 是一个泛型表达式。所以当且仅当 type 对应的类型是类似于 String[]、Integer[] 这样的数组时,type 的类型才是GenericArrayType。GenericArrayType 的函数:

1
Type getGenericComponentType();

这是 GenericArrayType 仅有一个函数,由于 getGenericComponentType 所代表的表达是 String[] 这种的数组,所以 getGenericComponentType 获取的就是这里的数组类型所对应的 Type,比如这里的 String[] 通过 getGenericComponentType 获取到的 Type 对应的就是 String。

好了,下面我们就举个例子来看看 GenericArrayType 的用法。我们重新生成一个泛型接口 PointSingleInterface:

1
2
public interface PointSingleInterface<T> {
}

这个泛型接口,只有一个泛型变量。然后生成一个类继承这个接口:

1
2
public class PointArrayImpl implements PointSingleInterface<Integer[]> {
}

在 PointArrayImpl 中,我们填充 PointSingleInterface 中泛型变量 T 的是 Integer[],一个 Integer 数组。下面我们来看看如何获取 PointArrayImpl 的接口信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Class<?> clazz = PointArrayImpl.class;
Type[] interfaces = clazz.getGenericInterfaces();
for (Type type : interfaces){
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type[] actualArgs = pt.getActualTypeArguments();
for (Type arg : actualArgs){
if (arg instanceof GenericArrayType){
GenericArrayType arrayType = (GenericArrayType)arg;
Type comType = arrayType.getGenericComponentType();
Class<?> typeClass = (Class)comType;
Log.d(TAG, "数组类型为:"+typeClass.getName());
}
}
}
}

执行结果:

分析:首先,通过 clazz.getGenericInterfaces() 获取 PointArrayImpl.class 的接口对应的 type 列表:

1
2
Class<?> clazz = PointArrayImpl.class;
Type[] interfaces = clazz.getGenericInterfaces();

我们知道 PointArrayImpl.class 只直接继承一个接口:PointSingleInterface<Integer[]>,所以 interfaces 数组中只有一个元素,它代表的表达式就是 PointSingleInterface<Integer[]>;明显这个一个泛型表达式,所以这个 type 的类型就是 ParameterizedType。

1
2
3
4
5
6
7
8
9
for (Type type : interfaces) {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type[] actualArgs = pt.getActualTypeArguments();
for (Type arg : actualArgs){
...
}
}
}

然后利用 for…each 对 interfaces 数组进行逐个列表,但我们知道它只有一个元素,代表的表达式是 PointSingleInterface;然后用

1
Type[] actualArgs = pt.getActualTypeArguments();

得到表达式中 PointSingleInterface<Integer[]> 的参数列表,显然参数只有一个,即 Integer[],所以 actualArgs 中只有一个元素,这个 type 元素对应的表达式是 Integer[]。我们知道当 type 对应的表达式是 Integer[] 时,这个 type 的类型就是 GenericArrayType。

1
2
3
4
5
6
if (arg instanceof GenericArrayType){
GenericArrayType arrayType = (GenericArrayType)arg;
Type comType = arrayType.getGenericComponentType();
Class<?> typeClass = (Class)comType;
Log.d(TAG, "数组类型为:" + typeClass.getName());
}

我们将 arg 强转为 GenericArrayType 类型的变量 arrayType,然后利用 arrayType.getGenericComponentType() 得到数组的类型,因为我们这里的数组是 Integer[],所以得到的类型是 Integer,明显这是一个确切的类,所以它的类型就是 Class,所以我们直接将 comType 进行强制转换为 Class<?> typeClass,最后利用 typeClass.getName() 得到 Integer 的具体类名。

好了,到这里,我们已经讲完了两种类型,下面开始讲解最后一个也是最难的一种类型 WildcardType。

3、WildcardType

3.1、概述

我们前面说过,当 type 所代表的表达式是类型通配符相关的表达式时,比如 <? extends Integer>、<? super String> 或者 <?> 等,这个 type 的类型就是 WildcardType。

我们先来看看 WildcardType 的函数:

1
2
3
4
// 获取上边界对象列表
Type[] getUpperBounds();
// 获取下边界对象列表
Type[] getLowerBounds();

  • getUpperBounds:获取上边界对象列表,上边界就是使用 extends 关键字所做的的限定,如果没有默认是 Object;
  • getLowerBounds:获取下边界对象列表,下边界是指使用 super 关键字所做的限定,如果没有则为 Null。

我们举个例子:
<? extends Integer> 这个通配符的上边界就是 Integer.Class,下边界就是 null。
<? super String> 这个通配符的下边界是 String,上边界就是 Object。

有关上下边界,大家可能很不好记,我画个图来给大家解释下,上下边界的含义:

看到这个类继承图,大家应该很容易就明白了,类继承图中,根结点始终是在祖先类,而且在继承图的上方,所以上方的就是上界,而子类是在下方,下方的就是下界。而表现在代码上,上界是继承的关系,所以是 <? extends Object>,而下界的则是 <? super Double>。

3.2、有关通配符的使用范围

通配符只是泛型变量的填充类型的一种,不能做为泛型变量使用。通配符 ? 只能出现在 Box<?> box; 中,其它位置都是不对的。即只能出现在生成泛型实例时使用,其它位置都是不可以的。尤其像下面这两个,直接用来填充类中的泛型变量:

但是第三个却是允许的。因为,这里的通配符 Comparable<? extends Number>,只有来生成 Comparable 对象的,所以是允许使用的。大家一定要注意,通配符只能用来填充泛型类来生成对象,其它用途一概是错误的。

3.3、举例说明

同样,我们使用上面的 PointSingleInterface 泛型接口:

1
public interface PointSingleInterface<T> {}

然后我们生成一个类来继承这个接口:

1
2
public class PointWildcardImpl implements PointSingleInterface<Comparable<? extends Number>> {
}

接下来就看看我们是怎么得到的 PointWildcardImpl 信息的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Class<?> clazz = PointWildcardImpl.class;
// 此时的 type 对应 PointSingleInterface<Comparable<? extends Number>>
Type[] types = clazz.getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 得到填充 PointSingleInterface 的具体参数,
// 即:Comparable<? extends Number>,仍然是一个 ParameterizedType
Type[] actualTypes = parameterizedType.getActualTypeArguments();
for (Type actualType : actualTypes) {
if (actualType instanceof ParameterizedType) {
ParameterizedType ComparableType = (ParameterizedType) actualType;
// 对 Comparable<? extends Number> 再取填充参数,
// 得到的 type 对应 <? extends Number>,这个就是 WildcardType 了
Type[] compareArgs = ComparableType.getActualTypeArguments();
for (Type Arg:compareArgs){
if(Arg instanceof WildcardType) {
// 将得到的对应 WildcardType 的 type 强转为 WildcardType 的变量
WildcardType wt = (WildcardType) Arg;
// 利用 getLowerBounds 得到下界,即派生自 Super 的限定,
// 如果没有派生自 super 则为 null
Type[] lowerBounds = wt.getLowerBounds();
for (Type bound:lowerBounds){
Class<?> boundClass = (Class)bound;
Log.d(TAG, "lowerBound为:" + boundClass.getName());
}
// 通过 getUpperBounds 得到上界,即派生 自extends 的限定,
// 如果没有,默认是 Object
Type[] upperBounds = wt.getUpperBounds();
for (Type bound:upperBounds) {
Class<?> boundClass = (Class)bound;
// 如果不写,则默认输出 Object,如果写了,则输出对应的
Log.d(TAG, "upperBound为:" + boundClass.getName());
}
}
}
}
}
}
}

执行结果为:

好了,到这里,所有有关 type 的类型就讲完了,但我们上面是逐个分析当前 type 应该强转为哪种类型的,如果我们稍微疏忽分析错了,或者,我们根本不知道它当前是哪种类型,这要怎么办,我们必须能写出来一个统一的转换函数出来。我们知道 type 所有的类型总共五种:Class、ParameterizedType、TypeVariable、WildcardType、GenericArrayType,所以我们利用递规的方法来写一个通用类型转换函数出来。

3.4、通用类型转换函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 入口
private void parseClass(Class<?> c) {
parseTypeParameters(c.getGenericInterfaces());
}
private void parseTypeParameters(Type[] types) {
for (Type type:types) {
parseTypeParameter(type);
}
}
private void parseTypeParameter(Type type) {
if (type instanceof Class) {
Class<?> c = (Class<?>) type;
Log.d(TAG, c.getSimpleName());
} else if(type instanceof TypeVariable) {
TypeVariable<?> tv = (TypeVariable<?>)type;
Log.d(TAG, tv.getName());
parseTypeParameters(tv.getBounds());
} else if (type instanceof WildcardType) {
WildcardType wt = (WildcardType)type;
Log.d(TAG, "?");
parseTypeParameters(wt.getUpperBounds());
parseTypeParameters(wt.getLowerBounds());
} else if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType)type;
Type t = pt.getOwnerType();
if (t != null) {
parseTypeParameter(t);
}
parseTypeParameter(pt.getRawType());
parseTypeParameters(pt.getActualTypeArguments());
} else if (type instanceof GenericArrayType) {
GenericArrayType arrayType = (GenericArrayType)type;
Type t = arrayType.getGenericComponentType();
parseTypeParameter(t);
}
}

使用上面的 PointWildcardImpl 类进行测试:

1
parseClass(PointWildcardImpl.class);

执行结果:

三、总结


本文涉及函数:

1
2
3
4
// 获取泛型超类的 Type
public Type getGenericSuperclass();
// 获取泛型接口的方法
public Type[] getGenericInterfaces();

ParameterizedType 相关:

1
2
3
4
// 获取填充泛型变量的真实参数列表
Type[] getActualTypeArguments();
// 返回声明此当前泛型表达式的类或接口的 Class 对象
Type getRawType();

TypeVariable 相关:

1
2
3
4
5
// 就是得到当前泛型变量的名称
String getName();
// 返回表示此类型变量上边界的 Type 对象的数组。
// 如果没有上边界,则默认返回Object
Type[] getBounds();

GenericArrayType 相关:

1
2
// 当前数组类型所对应的 Type
Type getGenericComponentType();

WildcardType 相关:

1
2
3
4
// 获取通配符的上边界对象列表
Type[] getUpperBounds();
// 获取通配符的下边界对象列表
Type[] getLowerBounds();

原文链接:
夯实JAVA基本之二 —— 反射(2):泛型相关周边信息获取