上节遗留两个知识点,分别是获取泛型的超类和获取泛型接口的方法:
//获取泛型的超类
public Type getGenericSuperclass();
//获取泛型接口的方法
public Type[] getGenericInterfaces();
这节具体介绍如何获取泛型的超类和接口。
1.获取泛型超类
我们先创建一个类和子类:
//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> {
}
这里PointImpl为非泛型类继承Point,并将泛型变量填充类型为Integer。下面, 我们将通过反射获取PointImpl的父类的类型,以及PointImpl的填充类型。
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());
}
代码看不懂?没关系,一步步分析,getGenericSuperclass()返回的是一个Type类型;我们看下Type是什么东东。查看源代码,可以看出它是一个接口:
package java.lang.reflect;
/**
* Common interface implemented by all Java types.
*
* @since 1.5
*/
public interface Type {
// Empty
}
并且Class类实现了这个接口,Type是用来标识,当前Class中所填充的类型的。意思是,当我们在填充一个泛型时,这个泛型就会保存在Type中,用到的时候再取出来。
但是问题是我们填充的类型如果是一个数组呢?比如:
public class PointArrayImpl extends Point<Integer[]> {
}
该怎么标识?所以Type就会派生不同的子类来分别标识不同的类型,它共有五种类型,分别是:
Class,ParameterizedType,TypeVariable,WildcardType,GenericArrayType。
- ParameterizedType:当type所代表的表达式是一个完整泛型时,比如Point,那这个Type类型就是ParameterizedType
- Class:如果type所代表的是一个确定的类,比如Integer,String,Double等,那这个type所对应的类型就是Class;强转之后,得到的就是他们所对应的Class对象,即Integer.Class,String.Class,Double.Class等。
- GenericArrayType:如果type对应的类型是类似于Integer[]的数组,那它的类型就是GenericArrayType;
- WildcardType: 如果type对应的是一个通配符表达式,比如? extends Num,或者仅仅是一个通配符?,类似这种有通符符的类型就是WildcardType
- 如果type对应的是一个泛型变量,即类似于T,或U这种还没有被填充的泛型变量,那它的类型就是TypeVariable
(1) ParameterizedType
ParameterizedType 有两个函数:
Type[] getActualTypeArguments();
Type getRawType();
getActualTypeArguments,用来返回当前泛型表达式中,用来填充泛型变量的真正值的列表。像我们这里得到的Point,用来填充泛型变量T的是Integer类型,所以这里返回的Integer类型所对应的Class对象。
getRawType: 它返回的值是com.harvic.blog_reflect_2.Point,所以它的意义就是声明当前泛型表达式的类或者接口的Class对象。比如,我们这里的type对应的是Point,而声明Point这个泛型的当然是Point类型。所以返回的是Point.Class。
2 获取泛型接口
//获取泛型接口的方法
public Type[] getGenericInterfaces();
和普通类获取泛型接口一样,泛型类获取泛型接口也是获取泛型类的直接接口。
首先,创建一个泛型接口:
public interface PointInterface<T,U> {
}
然后,我们直接使用前面的PointImpl来继承好了,就不再另写其它类了:
public class PointImpl extends Point<Integer> implements PointInterface<String,Double> {
}
我们将接口填充为String,Double类型
下面我们来看如何来获取PointImpl所继承的泛型接口的信息:
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());
}
}
因为我们知道,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()获取其完整的路径名。
3 Demo:写一个通用类型转换函数
(1)实现parseClass(Class<?> c)函数
先写一个parseClass的入口函数,用来得到他所直接继承的泛型接口:
private void parseClass(Class<?> c){
parseTypeParameters(c.getGenericInterfaces());
}
然后再实现parseTypeParameters函数,来解析Type[] 数组:
private void parseTypeParameters(Type[] types){
for(Type type:types){
parseTypeParameter(type);
}
}
然后利用paraseTypeParameter(type)对每一个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);
}
}
具体细节等下再讲,先看看怎么使用parseClass(Class<?>c);
(2)使用parseClass(Class<?>c)
首先创建PointSingleInterface泛型接口
public interface PointSingleInterface<T> {
}
然后生成一个类来实现这个接口
public class PointWildcardImpl implements PointSingleInterface<Comparable<? extends Number>> {
}
然后我们调用parseClass(Class<?> c)对它进行分析:
parseClass(PointWildcardImpl.class);
(3)parseClass(Class<?> c)实现详解
if(type instanceof Class){
Class<?> c = (Class<?>) type;
Log.d(TAG, c.getSimpleName());
}
如果Type是Class类型,说明type已经是一个确切的类了,不会再有下一层的参数填充,直接打印它的名称。
} else if(type instanceof TypeVariable){
TypeVariable<?> tv = (TypeVariable<?>)type;
Log.d(TAG, tv.getName());
parseTypeParameters(tv.getBounds());
}
如果type是TypeVarialble类型,说明这是一个泛型变量,得到的是T、U这类具有泛型变量的字母,我们可以先打印出来,然后再获取他们所有的上边界;
} else if(type instanceof WildcardType){
WildcardType wt = (WildcardType)type;
Log.d(TAG, "?");
parseTypeParameters(wt.getUpperBounds());
parseTypeParameters(wt.getLowerBounds());
}
如果type类型是WildcardType,即上面最难的通配符,因为只要是WildcardType,肯定是有问号的,但WildcardType是没有getName()函数来获取问号的标识的,所以我们只有手动输出一个问号标识了Log.d(TAG, “?”);同样通配符也是有上下边界的,比如<? extends xxx>,<? super xxx>,所以利用parseTypeParameters()分别来分析它的上边界type数组和下边界的type数组
} else if(type instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType)type;
parseTypeParameter(pt.getRawType());
parseTypeParameters(pt.getActualTypeArguments());
}
如果type类型是ParameterizeType类,则type代表的表达式是一个泛型;通过pt.getRawType()得到声明这个类的类型;比如这里的Comparable<? extends Number>,得到的将是Comparable.Class;然后利用pt.getActualTyupeArguments()获取这个泛型的具体参数的填充列表。同样利用parseTypeParameters再次分析;
} else if (type instanceof GenericArrayType){
GenericArrayType arrayType = (GenericArrayType)type;
Type t = arrayType.getGenericComponentType();
parseTypeParameter(t);
}
最后,是GenericArrayType类型,如果type代表的表达式是类似于String[],Integer[]这种数组的话,那type就是GenericArrayType类型。GenericArrayType只有一个函数getGenericComponentType(),得到这个数组的类型。同样将返回的type值传给parseTypeParameter(t)再次分析。