请选择 进入手机版 | 继续访问电脑版

雨滴科技技术论坛

 找回密码
 立即注册
查看: 38|回复: 0

反射基础二

[复制链接]

220

主题

1228

帖子

4856

积分

论坛元老

Rank: 8Rank: 8

积分
4856
扫一扫,手机访问本帖
发表于 2019-7-8 09:24:03 | 显示全部楼层 |阅读模式
  上节遗留两个知识点,分别是获取泛型的超类和获取泛型接口的方法:
  1. <font size="4">
  2. //获取泛型的超类
  3. public Type getGenericSuperclass();


  4. //获取泛型接口的方法
  5. public Type[] getGenericInterfaces();
复制代码


  这节具体介绍如何获取泛型的超类和接口。


1.获取泛型超类
  我们先创建一个类和子类:

  1. <font size="4">//Point泛型类的实现
  2. public class Point {
  3.     private T x,y;

  4.     public T getX() {
  5.         return x;
  6.     }

  7.     public void setX(T x) {
  8.         this.x = x;
  9.     }

  10.     public T getY() {
  11.         return y;
  12.     }

  13.     public void setY(T y) {
  14.         this.y = y;
  15.     }

  16. }
  17. //PointImpl类的实现
  18. public class PointImpl extends Point {
  19. }
复制代码
  这里PointImpl为非泛型类继承Point,并将泛型变量填充类型为Integer。下面, 我们将通过反射获取PointImpl的父类的类型,以及PointImpl的填充类型。
  1. <font size="4">Class clazz = PointImpl.class;
  2. Type type = clazz.getGenericSuperclass();
  3. if (type instanceof ParameterizedType) {
  4.     ParameterizedType parameterizedType = (ParameterizedType) type;
  5.     //返回表示此类型实际类型参数的 Type 对象的数组
  6.     Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
  7.     for (Type parameterArgType : actualTypeArguments) {
  8.         Class parameterArgClass = (Class) parameterArgType;
  9.         Log.d(TAG,"填充类型为:" + parameterArgClass.getName());
  10.     }

  11.     //返回 Type 对象,表示声明此类型的类或接口。
  12.     Type type1 = parameterizedType.getRawType();
  13.     Class class22 = (Class) type1;
  14.     Log.d(TAG,"PointImpl的父类类型为:"+class22.getName());

  15. }
复制代码
  代码看不懂?没关系,一步步分析,getGenericSuperclass()返回的是一个Type类型;我们看下Type是什么东东。查看源代码,可以看出它是一个接口:
  1. <font size="4">package java.lang.reflect;

  2. /**
  3. * Common interface implemented by all Java types.
  4. *
  5. * @since 1.5
  6. */
  7. public interface Type {
  8.     // Empty
  9. }
复制代码

   
  会保存在Type中,用到的时候再取出来。但是问题是我们填充的类型如果是一个数组呢?比如:
  1. <font size="4">public class PointArrayImpl extends Point {
  2. }
复制代码
  该怎么标识?所以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 有两个函数:


  1. <font size="4">Type[] getActualTypeArguments();
  2. Type getRawType();
复制代码
    getActualTypeArguments,用来返回当前泛型表达式中,用来填充泛型变量的真正值的列表。像我们这里得到的Point,用来填充泛型变量T的是Integer类型,所以这里返回的Integer类型所对应的Class对象。
getRawType: 它返回的值是com.harvic.blog_reflect_2.Point,所以它的意义就是声明当前泛型表达式的类或者接口的Class对象。比如,我们这里的type对应的是Point,而声明Point这个泛型的当然是Point类型。所以返回的是Point.Class。
2 获取泛型接口

  1. <font size="4">//获取泛型接口的方法
  2. public Type[] getGenericInterfaces();
复制代码


然后,我们直接使用前面的PointImpl来继承好了,就不再另写其它类了:
  1. <font size="4">public class PointImpl extends Point implements PointInterface {
  2. }
复制代码
我们将接口填充为String,Double类型
下面我们来看如何来获取PointImpl所继承的泛型接口的信息:
  1. <font size="4">Class clazz = PointImpl.class;
  2. Type[] types = clazz.getGenericInterfaces();
  3. for (Type type:types) {
  4.     if (type instanceof ParameterizedType) {
  5.         ParameterizedType parameterizedType = (ParameterizedType) type;
  6.         //返回表示此类型实际类型参数的 Type 对象的数组
  7.         Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
  8.         for (Type parameterArgType : actualTypeArguments) {
  9.             Class parameterArgClass = (Class) parameterArgType;
  10.             Log.d(TAG, "此接口的填充类型为:" + parameterArgClass.getName());
  11.         }

  12.         //返回 Type 对象,表示声明此类型的类或接口。
  13.         Type type1 = parameterizedType.getRawType();
  14.         Class class22 = (Class) type1;
  15.         Log.d(TAG,"声明此接口的类型为:"+class22.getName());
  16.     }
  17. }
复制代码


  因为我们知道,PointInterface被PointImpl填充为PointInterface,所以它的真实的参数类型应该是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的接口类类型,虽然这里得到的是Type,但我们声明接口的是PointInterface.Class所以,也是Class类型,直接将其强转为Class即可。最后通过Class.getName()获取其完整的路径名。


3 Demo:写一个通用类型转换函数

(1)实现parseClass(Class c)函数
先写一个parseClass的入口函数,用来得到他所直接继承的泛型接口:
  1. <font size="4">private void parseClass(Class c){
  2.     parseTypeParameters(c.getGenericInterfaces());
  3. }
复制代码


然后再实现parseTypeParameters函数,来解析Type[] 数组:
  1. <font size="4">private void parseTypeParameters(Type[] types){
  2.     for(Type type:types){
  3.         parseTypeParameter(type);
  4.     }
  5. }
复制代码
然后利用paraseTypeParameter(type)对每一个type进行分析:



  1. <font size="4">private void parseTypeParameter(Type type){
  2.     if(type instanceof Class){
  3.         Class c = (Class) type;
  4.         Log.d(TAG, c.getSimpleName());
  5.     } else if(type instanceof TypeVariable){
  6.         TypeVariable tv = (TypeVariable)type;
  7.         Log.d(TAG, tv.getName());
  8.         parseTypeParameters(tv.getBounds());
  9.     } else if(type instanceof WildcardType){
  10.         WildcardType wt = (WildcardType)type;
  11.         Log.d(TAG, "?");
  12.         parseTypeParameters(wt.getUpperBounds());
  13.         parseTypeParameters(wt.getLowerBounds());
  14.     } else if(type instanceof ParameterizedType){
  15.         ParameterizedType pt = (ParameterizedType)type;
  16.         Type t = pt.getOwnerType();
  17.         if(t != null) {
  18.             parseTypeParameter(t);
  19.         }
  20.         parseTypeParameter(pt.getRawType());
  21.         parseTypeParameters(pt.getActualTypeArguments());
  22.     } else if (type instanceof GenericArrayType){
  23.         GenericArrayType arrayType = (GenericArrayType)type;
  24.         Type t = arrayType.getGenericComponentType();
  25.         parseTypeParameter(t);
  26.     }
  27. }
复制代码


具体细节等下再讲,先看看怎么使用parseClass(Classc);
(2)使用parseClass(Classc)
首先创建PointSingleInterface泛型接口


  1. <font size="4">public interface PointSingleInterface {
  2. }
复制代码
然后生成一个类来实现这个接口


  1. <font size="4">public class PointWildcardImpl implements PointSingleInterface<Comparable<? extends Number>> {
  2. }
复制代码
然后我们调用parseClass(Class c)对它进行分析:



  1. <font size="4">parseClass(PointWildcardImpl.class);
复制代码
(3)parseClass(Class c)实现详解


  1. <font size="4">if(type instanceof Class){
  2.     Class c = (Class) type;
  3.     Log.d(TAG, c.getSimpleName());
  4. }
复制代码


如果Type是Class类型,说明type已经是一个确切的类了,不会再有下一层的参数填充,直接打印它的名称。


  1. <font size="4">} else if(type instanceof TypeVariable){
  2.     TypeVariable tv = (TypeVariable)type;
  3.     Log.d(TAG, tv.getName());
  4.     parseTypeParameters(tv.getBounds());
  5. }
复制代码
如果type是TypeVarialble类型,说明这是一个泛型变量,得到的是T、U这类具有泛型变量的字母,我们可以先打印出来,然后再获取他们所有的上边界;


  1. <font size="4">} else if(type instanceof WildcardType){
  2.     WildcardType wt = (WildcardType)type;
  3.     Log.d(TAG, "?");
  4.     parseTypeParameters(wt.getUpperBounds());
  5.     parseTypeParameters(wt.getLowerBounds());
  6. }
复制代码
如果type类型是WildcardType,即上面最难的通配符,因为只要是WildcardType,肯定是有问号的,但WildcardType是没有getName()函数来获取问号的标识的,所以我们只有手动输出一个问号标识了Log.d(TAG, “?”)
同样通配符也是有上下边界的,比如,,所以利用parseTypeParameters()分别来分析它的上边界type数组和下边界的type数组


  1. <font size="4">} else if(type instanceof ParameterizedType){
  2.     ParameterizedType pt = (ParameterizedType)type;
  3.     parseTypeParameter(pt.getRawType());
  4.     parseTypeParameters(pt.getActualTypeArguments());
  5. }
复制代码
如果type类型是ParameterizeType类,则type代表的表达式是一个泛型;通过pt.getRawType()得到声明这个类的类型;比如这里的Comparable,得到的将是Comparable.Class;然后利用pt.getActualTyupeArguments()
获取这个泛型的具体参数的填充列表。同样利用parseTypeParameters再次分析;
  1. <font size="4">} else if (type instanceof GenericArrayType){
  2.     GenericArrayType arrayType = (GenericArrayType)type;
  3.     Type t = arrayType.getGenericComponentType();
  4.     parseTypeParameter(t);
  5. }
复制代码


最后,是GenericArrayType类型,如果type代表的表达式是类似于String[],Integer[]这种数组的话,那type就是GenericArrayType类型。GenericArrayType只有一个函数getGenericComponentType(),得到这个数组的类型。同样将返回的type值传
给parseTypeParameter(t)再次分析。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

在线客服
在线咨询
咨询热线
0755-26787502-8006/8016
扫一扫二维码
直接访问本站

QQ|Archiver|手机版|小黑屋|雨滴科技  

GMT+8, 2019-7-19 04:15 , Processed in 0.066905 second(s), 12 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表