反射基础二

上节遗留两个知识点,分别是获取泛型的超类和获取泛型接口的方法:


//获取泛型的超类
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。

  1. ParameterizedType:当type所代表的表达式是一个完整泛型时,比如Point,那这个Type类型就是ParameterizedType
  2. Class:如果type所代表的是一个确定的类,比如Integer,String,Double等,那这个type所对应的类型就是Class;强转之后,得到的就是他们所对应的Class对象,即Integer.Class,String.Class,Double.Class等。
  3. GenericArrayType:如果type对应的类型是类似于Integer[]的数组,那它的类型就是GenericArrayType;
  4. WildcardType:  如果type对应的是一个通配符表达式,比如? extends Num,或者仅仅是一个通配符?,类似这种有通符符的类型就是WildcardType
  5. 如果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)再次分析。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值