----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
以前对Method的讨论中,我们主要是通过Method类对象对Method对象关联的方法的调用的讨论。今天我们通过Method类对象对自身的组成部分( 返回值类型 和 参数类型)进行剖析。
【前提】相应Type子接口实现子类的实例已经获取到,并强转成相应的子类接口实现类的实例。
(1). 参数化类型 (ParameterizedType) 的细节信息
[1]. 参数化类型构成元素:原始类型 和类型变量/替代类型变量的实际类型。
[2]. 在强转成参数化类型对象后,分别采用(2)和(3)中的方式进行细节的获取。
(2). 原始类型的获取
通过ParameterizedType的getRawType()获取参数化类型的原始类型
(3). 类型变量/替代类型变量的实际类型的获取
通过ParameterizedType的getActualTypeArguments()获取参数化类型的类型变量/替代类型变量的实际类型
{1}.参数化类型(ParameterizedType) :原始类型 +类型变量/替代类型变量的实际类型
强转成参数化类型对象后,分别采用不同方法来提取不同的细致信息
{1}1. 通过ParameterizedType对象的getRawType()获取参数化类型的原始类型
{1}2. 通过ParameterizedType对象的getActualTypeArguments()获取参数化类型的类型变量/替代类型变量的实际类型。
(1). 泛型数组类型 (GenericArrayType) 的细节信息
[1]. 泛型数组类型构成元素:元素类型 和 数组声明操作符 [ ] (可能会有多个[ ])。
[2]. 在强转成泛型数组类型对象后,分别采用(2)中的方式进行细节的获取。
(2). 数组成员类型的获取
通过GenericArrayType的getGenericComponentType()获取泛型数组类型的成员类型。
(1). 类型变量(TypeVariable) 的细节信息
[1]. 类型变量自身没有构成元素,但是在类型变量定义的时候,可以通过extends为类型变量指定多个父类。
[2]. 在强转成类型变量对象后,分别采用(2)中的方式进行细节的获取。
(2). 类型变量上边界的获取
通过TypeVariable的getBounds()获取类型变量定义时候的上边界的父类类型。
(1). 通配符表达式类型 (WildcardType) 的细节信息
[1]. 通配符表达式的构成元素:通配符? +extends/super关键字+ 上边界/下边界类型。
【注意】这里面通配符和泛型限定关键字本身没必要获取,因为这是固定的。所以需要通过程序获得的细节部分是关键字后边的上边界或者下边界的类型
[2]. 在强转成通配符表达式对象后,分别采用(2)中的方式进行细节的获取。
(2). 通配符表达式类型上边界/下边界的获取
通过WildcardType的getUpperBounds()和getLowerBounds()分别获取上边界的父类类型或者下边界的子类类型。
【注意】对于获取细节过程中,一旦是返回值类型是Type或者Type[ ]的结果,由于类型本身还是不确定的,所以还可以继续强制转换成相应子接口实现类的实例,进行更细一步的操作。
以下的结果还可以继续强转细化
(1). 参数化类型ParameterizedType的实际参数类型
getActualTypeArguments()返回Type[]
(2). 泛型数组类型GenericArrayType的元素类型
getGenericComponentType()返回Type
(3). 定义类型变量TypeVariable的上边界
getBounds()返回Type[]
(4). 通配符表达式WildcardType的上边界父类/下边界子类
getUpperBounds()返回Type[]
getLowerBounds()返回Type[]
(1). Java中的函数的组成部分
Java中,函数由各种修饰符(访问修饰符,静态修饰符,抽象修饰符,同步修饰符)、返回值类型、方法签名、参数列表和函数体组成。【异常不做讨论先】
(2). 与泛型有关的类型在函数中出现的位置
[1]. 在函数中,与泛型有关的类型最容易出现在函数的返回值类型和函数的参数列表中。
[2]. 所以在方法对应的Method类对象中,Java反射对与泛型有关的类型的解析也主要是集中在Method对象的返回值类型(ReturnType) 和Method对象的参数类型这2个方面
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 mTest(E e, int i, String str,ArrayList 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
获取普通参数方法:将泛型参数进行了擦除操作,将<>中的类型直接抹去,仅仅保留参数化类型中的原始类型部分。
(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 mTestII(E e, int i, String str, ArrayList 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的时候是
[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)
【1】1.这个类型变量在定义的时候有泛型限定,那普通xx方法就使用extends指定的上界父类来替代这个类型变量。
【注意】一定是定义类型变量的时候存在泛型限定而不是使用的时候!!!!
【1】2. 这个类型变量在定义的时候有不存在泛型限定,那普通xx方法就使用Object类型来替代这个类型变量。
【2】如果擦去的泛型中含有<>,直接去掉包括<>本身及其中所有参数,无论<>里面是什么内容。
【总结】就是普通xx方法要将与泛型有关的类型翻译成/擦除到原始类型。
擦除的程度是什么样呢?如果在定义类型变量时候没有使用泛型限定,那么这种擦除程度最大,直接擦除到Object类型。否则擦除到在定义泛型变量定义时候由extends指定的上界类型的父类类型。
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------