关闭

黑马程序员--Java基础加强--14.利用反射操作泛型III【解析关于泛型类型的细节信息的获取方法】【Method与泛型相关的方法】【个人总结】

1597人阅读 评论(0) 收藏 举报
分类:

利用反射操作泛型III

解析关于泛型类型的细节信息的获取方法 Method与泛型相关的方法

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

以前对Method的讨论中,我们主要是通过Method类对象Method对象关联的方法的调用的讨论。今天我们通过Method类对象对自身的组成部分( 返回值类型参数类型)进行剖析

1.    解析关于泛型类型的细节信息的获取方法

【前提】相应Type子接口实现子类的实例已经获取到,并强转相应的子类接口实现类的实例

1). 解析ParameterizedType细节

(1). 参数化类型 (ParameterizedType) 的细节信息

[1]. 参数化类型构成元素原始类型类型变量/替代类型变量的实际类型。

[2]. 在强转成参数化类型对象后,分别采用(2)和(3)中的方式进行细节的获取。

(2). 原始类型的获取

通过ParameterizedType的getRawType()获取参数化类型原始类型

(3). 类型变量/替代类型变量的实际类型的获取

通过ParameterizedType的getActualTypeArguments()获取参数化类型类型变量/替代类型变量的实际类型

{1}.参数化类型(ParameterizedType) :原始类型 +类型变量/替代类型变量的实际类型

强转成参数化类型对象后,分别采用不同方法来提取不同的细致信息

{1}1. 通过ParameterizedType对象的getRawType()获取参数化类型原始类型

{1}2. 通过ParameterizedType对象的getActualTypeArguments()获取参数化类型类型变量/替代类型变量的实际类型。

2). 解析GenericArrayType细节

(1). 泛型数组类型 (GenericArrayType) 的细节信息

[1]. 泛型数组类型构成元素元素类型数组声明操作符 [ ] (可能会有多个[ ])

[2]. 在强转成泛型数组类型对象后,分别采用(2)中的方式进行细节的获取。

(2). 数组成员类型的获取

通过GenericArrayType的getGenericComponentType()获取泛型数组类型成员类型。

3). 解析TypeVariable细节

(1). 类型变量(TypeVariable) 的细节信息

[1]. 类型变量自身没有构成元素,但是在类型变量定义时候,可以通过extends类型变量指定多个父类

[2]. 在强转成类型变量对象后,分别采用(2)中的方式进行细节的获取。

(2). 类型变量上边界的获取

通过TypeVariable的getBounds()获取类型变量定义时候上边界的父类类型。

4). 解析WildcardType细节

(1). 通配符表达式类型 (WildcardType) 的细节信息

[1]. 通配符表达式的构成元素通配符? +extends/super关键字+ 上边界/下边界类型

【注意】这里面通配符泛型限定关键字本身没必要获取因为这是固定的。所以需要通过程序获得的细节部分关键字后边的上边界或者下边界的类型

[2]. 在强转成通配符表达式对象后,分别采用(2)中的方式进行细节的获取。

(2). 通配符表达式类型上边界/下边界的获取

通过WildcardType的getUpperBounds()和getLowerBounds()分别获取上边界的父类类型或者下边界的子类类型。

5). 总结

【注意】对于获取细节过程中,一旦是返回值类型Type或者Type[ ]的结果,由于类型本身还是不确定的,所以还可以继续强制转换成相应子接口实现类的实例进行更细一步的操作

以下的结果还可以继续强转细化

(1). 参数化类型ParameterizedType的实际参数类型

getActualTypeArguments()返回Type[]

(2). 泛型数组类型GenericArrayType的元素类型

getGenericComponentType()返回Type

(3). 定义类型变量TypeVariable的上边界

getBounds()返回Type[]

(4). 通配符表达式WildcardType的上边界父类/下边界子类

getUpperBounds()返回Type[]

getLowerBounds()返回Type[]

2.    Method与泛型相关的方法

1). Java中的函数

(1). Java中的函数的组成部分

Java中,函数由各种修饰符(访问修饰符,静态修饰符,抽象修饰符,同步修饰符)、返回值类型方法签名参数列表和函数体组成。【异常不做讨论先】

(2). 与泛型有关的类型在函数中出现的位置

[1]. 在函数中,与泛型有关的类型最容易出现函数的返回值类型函数的参数列表中。

[2]. 所以在方法对应的Method类对象中,Java反射对与泛型有关的类型的解析也主要是集中Method对象的返回值类型(ReturnType) 和Method对象的参数类型2个方面


2). Method类中解析泛型的方法

Method中提供了2种方法获取参数列表类型对象普通/泛型】 + 2种方法获取返回值类型对象普通/泛型】+ 1种方法获取泛型方法中定义的类型变量

(1). 获取方法的普通参数类型数组

[1]. 源码声明

public Class<?>[] getParameterTypes();

[2]. 返回值类型Class元素类型的数组 Class<?>[]

分析

{1}. 元素个数分析:

由于方法参数的个数可能有多个,所以返回值类型是数组最准确。

{2}. 元素类型分析:

由于返回的全部是普通类型的参数[因为泛型擦除后,剩余的全是],这个就对应着Class [上一篇日志提到了普通类型或者原始类型对应的就是Class]。到底是什么类的Class?不知道,不一定。因为什么方法有什么样的参数是未知的,所以准确地写成Class<?>综合:Class<?>[]

(2). 获取方法的泛型参数类型数组

[1]. 源码声明

public Type[] getGenericParameterTypes();

[2]. 返回值类型Type元素类型的数组 Type[]

分析

{1}. 返回值类型元素个数分析数组类型【理由同上】。

{2}. 返回值元素类型:因为方法的参数可能是

普通参数类型 (Class)类型变量 (TypeVariable), 参数化参数类型 (ParameterizedType)泛型数组类型 (GenericArrayType)采用多态统一原则,使用了这些类型的直接公共父类Type类型进行统一。综上:Type[ ]

(3). 获取方法的普通返回值类型数组

[1]. 源码声明public Class<?> getReturnType();

[2]. 返回值类型Class<?>

分析

{1}. 返回值数量

由于方法的返回值只有一个,所以返回值类型也就是一个,所以不是数组

{2}. 返回值类型:由于是获取方法的普通参数,所以就是擦除后的参数。由前面的分析,很容易知道是Class<?>

(4). 获取方法泛型返回值类型数组

[1]. 源码声明public Type getGenericReturnType();

[2]. 返回值类型Type

分析】不用分析。

(5). getGenericParameterTypes() 和 getParameterTypes()实例比较

[1]. 示例代码

{1}. 测试的泛型方法及其所在的类

class TT{
    public static <E> E mTest(E e, int i, String str,ArrayList<E> al, ArrayList<? extends Number> ai2){
       return e;
    }
}

{2}.测试代码

Class clazz =TT.class;
Method method =clazz.getMethod("mTest", Object.class, int.class, String.class, ArrayList.class, ArrayList.class);
 
Type[] genericParameterTypes=method.getGenericParameterTypes();
Class[] commonParameterTypes=method.getParameterTypes();
 
System.out.println("genericParameterTypes.length==commonParameterTypes.length:"+(genericParameterTypes.length ==commonParameterTypes.length));
 
for(int i=0; i< genericParameterTypes.length; i++){
    System.out.println("Generic Type: "+ genericParameterTypes[i] +"** Common Type: "+ commonParameterTypes[i]);
}

[2].打印结果


{1}. 打印第一行:证明获取泛型参数方法获取普通参数方法返回的数组长度是一样的

{2}. 打印第二行:Generic Type:E** Common Type: class java.lang.Object

获取普通参数方法:将泛型参数进行了擦除操作,将类型变量E擦除到最大限度java.lang.Object

{3}. 打印第三行:Generic Type:int** Common Type: int

    获取泛型参数方法:对普通参数进行了直接保留操作。

{4}. 打印第五行:Generic Type:java.util.ArrayList<E>** Common Type: class java.util.ArrayList

    获取普通参数方法:将泛型参数进行了擦除操作,将<>中的类型直接抹去仅仅保留参数化类型中的原始类型部分

(6). getGenericReturnType() 和 getReturnType()实例比较

[1]. 示例代码1

{1}. 测试方法及其所在的类和(5)中的是一样的。

{2}. 测试代码:

Class clazz =TT.class;
Method method =clazz.getMethod("mTest", Object.class, int.class, String.class, ArrayList.class, ArrayList.class); 
Type genericReturnType=method.getGenericReturnType();
Class commonReturnType=method.getReturnType();
 
System.out.println(genericReturnType +"**"+commonReturnType);

{3}.打印结果

E**class java.lang.Object

获取普通参数方法:将泛型参数进行了擦除操作,擦除到最大程度Object

 (7). 采用泛型限定的类型变量出现的异常

[1]. 测试方法及其所在的类

class TT{
    public static <E extends Number> E mTestII(E e, int i, String str, ArrayList<E> al,ArrayList<? extends Number> ai2){
       return e;
    }
}

[2]. 测试代码

Class clazz =TT.class;
Method method =clazz.getMethod("mTestII", Object.class, int.class, String.class, ArrayList.class, ArrayList.class);
Type genericReturnType=method.getGenericReturnType();
Class commonReturnType=method.getReturnType();
System.out.println(genericReturnType +"**"+commonReturnType);

[3]. 打印结果却抛出异常


分析】指出clazz.getMethod("mTestII", Object.class, int.class, String.class, ArrayList.class, ArrayList.class);指定的方法是不存在的。原因是什么?

测试手段采用getMethods()让程序自动打印出mTestII方法的所有参数类型,查看一下。

测试代码

Class clazz =TT.class;
Method[] methods =clazz.getMethods();
for(Method method: methods){
    System.out.println(method);
}

测试结果】(仅截取部分)


【结论】定义时含有泛型限定的类型变量:被javac仅仅擦除到父边界类型。

这里边定义类型变量E的时候是<Eextends Number>  这个E仅仅被擦除草Number类型而不是总的父类型Object。

[4]. 修正测试代码

Class clazz =TT.class;
Method[] methods =clazz.getMethods();
Method method =clazz.getMethod("mTestII", Number.class, int.class, String.class, ArrayList.class, ArrayList.class);
Type genericReturnType=method.getGenericReturnType();
Class commonReturnType=method.getReturnType();
System.out.println(genericReturnType +"**"+commonReturnType);

[5]. 修正后的打印结果

E**class java.lang.Number

结论印证了获取普通参数方法:将泛型参数进行了擦除操作。

{1}. 如果定义的类型变量没有使用泛型限定,则获取普通参数方法类型变量擦除到最大程度所有类的父类类型Object

{2}. 如果定义的类型变量使用泛型限定,则获取普通参数方法仅仅类型变量擦除到定义类型变量是通过extends指定的父类类型

(8). getGenericXxType[s]()和getXxType[s]()规律

[1]. xx的取值Parameter或者Return

[2]. 前提假设普通参数是没有类型变量的泛型参数,也就是普通参数泛型参数特例

[3]. getGenericXxType[s]() getXxType[s]()的相同点返回相同长度的数组

普通xx方法 泛型xx方法:如果返回的是数组类型,那么普通xx方法 泛型xx方法这两个方法返回的这两个数组的长度一定是一样的

[4]. getGenericXxType[s]() getXxType[s]()的不同点交叉处理不同

{4}1. 泛型xx方法:如果碰见的方法中的参数或者返回值类型普通类型泛型xx方法普通类型参数或者返回值保留下来【依据的是普通参数泛型参数特例这条原则

{4}2. 普通xx方法:如果碰见的方法中的参数或者返回值类型泛型类型普通xx方法泛型类型参数或者返回值擦去相应的泛型信息,分情况处理:

【1】被擦除的是单独的类型变量 (TypeVariable)

11.这个类型变量定义的时候有泛型限定,那普通xx方法就使用extends指定的上界父类来替代这个类型变量。

注意】一定是定义类型变量的时候存在泛型限定而不是使用的时候!!!!

12. 这个类型变量定义的时候有不存在泛型限定,那普通xx方法就使用Object类型来替代这个类型变量。

【2】如果擦去的泛型中含有<>直接去掉包括<>本身及其中所有参数,无论<>里面是什么内容。

【总结】就是普通xx方法要将与泛型有关的类型翻译成/擦除到原始类型

擦除的程度是什么样呢?如果在定义类型变量时候没有使用泛型限定,那么这种擦除程度最大,直接擦除到Object类型否则擦除到在定义泛型变量定义时候extends指定的上界类型的父类类型

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    文章存档
    最新评论