前言:坚信自己坚信的,坚持自己坚持的,永远选择相信自己。
在上篇中,我们简单给大家讲解了如何利用反射来获取普通类型的类的使用,今天给大家讲解下,有关如何使用反射来获取泛型中的信息。提前提个醒,本篇文章内容稍难,大家可能需要多看几篇。
这篇文章将大量用到泛型的知识,如果对泛型声明及填充不太了解的同学,请先看完《夯实JAVA基本之一 —— 泛型详解系列》
//针对泛型父类而设计 public Type getGenericSuperclass();下面我们就先看看这个函数怎么用,我们依然以上篇中的Point类以及它的派生类PointImpl为例:
//Point泛型类的实现 public class Point从上面的代码中,我们可以看到,Point类是一个泛型类,具有一个泛型变量T;而PointImpl派生自Point并且在派生时,将Point进行填充为Point,即将Point中的泛型变量填充为Integer类型。{ 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 { }
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()); }相信上面这段代码,大家肯定是很不懂的。。。。因为确实狠复杂,不管那些,我们先看看结果:
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()); } }
Class clazz = PointImpl.class; Type type = clazz.getGenericSuperclass();在这段代码中,我们通过clazz.getGenericSuperclass()获取PointImpl.class的超类。由于我们知道PointImpl.class的父类是泛型,所以我们只能使用clazz.getGenericSuperclass()来获取。但获取出来的类型确是很让人捉急。一个Type类型,下面我们先间断下,讲讲这个Type类型是个什么鬼。
package java.lang.reflect; /** * Common interface implemented by all Java types. * * @since 1.5 */ public interface Type { // Empty }Type是一个接口,这里意思是它是Java所有类型都会继承这个接口。但通过源码会发现String,Integer,Double这些类都没有继承这个接口,就连Object也没继承!
public final class Class所以说,这个Type类型是泛型所特有的。那它用是来做什么的呢?implements Serializable, AnnotatedElement, GenericDeclaration, Type { ………… }
public class PointImpl extends Point这个填充类型就会放在Type的保存起来,当需要用到的时候再取出来。那问题又来了,我们这里填充的是Integer类型,那如果我们填充的是数组泛型呢,比如Point{ }
Class clazz = PointImpl.class; Type type = clazz.getGenericSuperclass();获得PointImpl.class的父类,而它的父类是Point,这明显是一个泛型类型,所以它对应的类型就是ParameterizedType;TypeVariable:这个代表的就是泛型变量,例如Point,这里面的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。这一段看不大明白也没关系,后面还会再讲。
Class clazz = PointImpl.class; Type type = clazz.getGenericSuperclass();这时的type对应的完整表达式就是:Point
Type[] getActualTypeArguments(); Type getRawType();getActualTypeArguments():用来返回当前泛型表达式中,用来填充泛型变量的真正值的列表。像我们这里得到的Point,用来填充泛型变量T的是Integer类型,所以这里返回的Integer类型所对应的Class对象。(有关这一段,下面会补充,这里先看getRawType)getRawType():我们从我们上面的代码中,也可以看到,它返回的值是com.harvic.blog_reflect_2.Point,所以它的意义就是声明当前泛型表达式的类或者接口的Class对象。比如,我们这里的type对应的是Point,而声明Point这个泛型的当然是Point类型。所以返回的是Point.Class
下面我们再回过来看看getActualTypeArguments():
我们上面说到,这个函数将返回用来填充泛型变量真实参数列表。像我们这里的是Point,将返回Integer对应的Class对象。而并不是所有的每次都会返回填充类型对应的Class对象。我们知道我们在填充一个泛型时,是存在各种可能的,比如Point,Point,Point
虽然我们没办法穷举可能填充为哪些类型,但我们知道Type类型是用来表示填充泛型变量的类型的,而继承Type接口只有下面五个:Class,ParameterizedType,TypeVariable,WildcardType,GenericArrayType!
所以这也是Type[] getActualTypeArguments();中Type[]数组的所有可能取值!>
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 = clazz.getGenericSuperclass();之后,得到的type的值对应的是:Point,所以我们知道,type对应的是ParameterizedType,所以我们用
if (type instanceof ParameterizedType) { ………… }来识别,然后将type变量强转为ParameterizedType变量:
ParameterizedType parameterizedType = (ParameterizedType) type;然后到了最重要的两句:
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type parameterArgType : actualTypeArguments) { Class parameterArgClass = (Class) parameterArgType; Log.d(TAG,"填充类型为:" + parameterArgClass.getName()); }然后,先利用parameterizedType.getActualTypeArguments()获取当前泛型变量的填充列表,我们知道Point中泛型变量T被填充为Integer,所以我们得到的数组Type[]里,只有一个值,它对应的就是Integer.Class。
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.harvic.blog_reflect_2.Point
//获取泛型接口的方法 public Type[] getGenericInterfaces();这里提前强调一点:大家需要注意是getGenericInterfaces()数与Class.getInterfaces()函数一样,都只能获取此类直接继承的接口列表!
public interface PointInterface可以看到,我们这个泛型接口里有两个泛型变量,这个接口里我们没有定义任何的方法,因为我们这里只会获取填充泛型接口的实际类型,不会用到它的方法,所以就没有必要生成了,写个空接口即可。{ }
public class PointImpl extends Point从这里可以看出,我们在生成PointImpl时将 PointInterfaceimplements PointInterface { }
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()); } }依然是一长段让人受不了的代码,我们一点点来分析。
Class clazz = PointImpl.class; Type[] types = clazz.getGenericInterfaces();因为我们知道,我们的PointImpl只继承了一个接口:PointInterface
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代表的是PointInterface
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type parameterArgType : actualTypeArguments) { Class parameterArgClass = (Class) parameterArgType; Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName()); }因为我们知道,PointInterface
//返回 Type 对象,表示声明此类型的类或接口。 Type type1 = parameterizedType.getRawType(); Class class22 = (Class) type1; Log.d(TAG,"声明此接口的类型为:"+class22.getName());好了,到这里,有关泛型超类和继承接口的信息获取到这就结束了,下面我们再来看看上面另外提到的另外三个Type类型:TypeVariable,WildcardType,GenericArrayType
String getName(); Type[] getBounds();getName:就是得到当前泛型变量的名称;getBounds:返回表示此类型变量上边界的 Type 对象的数组。如果没有上边界,则默认返回Object;有关这两个函数我们举个例子来详细说明:
public interface PointInterface这里,我们在PointInterface的基础上,重写一个类PointGenericityImpl,与上面直接在类中填充不同的是,它是一个泛型类,首先,将PointInterface{ } public class PointGenericityImpl implements PointInterface { }
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()); } } } }先看看结果:
Class clazz = PointGenericityImpl.class; Type[] types = clazz.getGenericInterfaces();我们知道PointGenericityImpl只直接继承了一个接口:PointInterface
ParameterizedType parameterizedType = (ParameterizedType) type; //返回表示此类型实际类型参数的 Type 对象的数组 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();我们知道,PointInterface
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()获取这个填充的泛型变量的名字,得到的值为:
然后,利用typeVariable.getBounds()得到T的限定条件:上边界的数组。上边界的意思就是extends关键字后面的限定条件。“上”的意思就是能取到的最大父类。最大父类当然是用extends关键字来限定的。我们知道这里的T的限定条件是:
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()); } } } }因为PointInterface
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();中第一个类型是type类型对应的是T,第二个type类型则是Integer类型。明显Integer是一个Class类型,所以我们直接将它强转为Class即可
if (parameterArgType instanceof Class){ Class parameterArgClass = (Class) parameterArgType; Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName()); }结果为:
Type getGenericComponentType();getGenericComponentType:这是GenericArrayType仅有一个函数,由于getGenericComponentType所代表的表达是String[]这种的数组,所以getGenericComponentType获取的就是这里的数组类型所对应的Type,比如这里的String[]通过getGenericComponentType获取到的Type对应的就是String.
public interface PointSingleInterface这个泛型接口,只有一个泛型变量{ }
public class PointArrayImpl implements PointSingleInterface在PointArrayImpl中,我们填充PointSingleInterface中泛型变量T的是Integer[],一个Integer数组!{ }
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列表
Class clazz = PointArrayImpl.class; Type[] interfaces = clazz.getGenericInterfaces();我们知道PointArrayImpl.class只直接继承一个接口:PointSingleInterface
for (Type type:interfaces){ if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; Type[] actualArgs = pt.getActualTypeArguments(); for (Type arg:actualArgs){ ………… } } }然后利用for…each对interfaces数组进行逐个列表,但我们知道它只有一个元素,代表的表达式是PointSingleInterface
Type[] actualArgs = pt.getActualTypeArguments()得到表达式中PointSingleInterface
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进行强制转换为ClasstypeClass,最后利用typeClass.getName()得到Integer的具体类名。