利用反射操作泛型IV-----
通过反射Method解析泛型方法思路
通过Method对四种Type子接口类型进行解剖
使用递归对任意复合泛型类型进行彻底解剖
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. 通过反射Method解析泛型方法思路
解析总体思路
想通过反射获取方法的参数类型:
step1. 将类的方法名和Method对象进行第一次关联
[1].获取到了Method类对象,这个类对象就是反射范围的,已经构成了使用反射的条件。
[2]. 此时一旦获取到Method类的对象,这个方法的总体信息就获取了。
step2. 通过Method类对象以下方法获取方法的参数类型数组或者返回值类型
[1]. getGenericParameterType()、getGenericReturnType( )
[2]. 通过上面的两种方法获得的就是Type实现子类或Class实现子类的实例或数组实例。
[3]. 如果是想获取和泛型有关的类型的各部分信息,先将Type类型的元素强制转换成子类类型接口实现子类的对象 (ParameterizeType,TypeVariable, GenericType, WildcardType, Class)。有了这些Type子类接口实现子类的对象,才能使用子接口提供的特有的方法来提取这个类型中更细致的部分信息。
step3. 通过Type不同子类对象提供的各自的方法进行细致内容的解析
【Class中的参数获取在最后一节进行阐述】
2. 通过Method对四种Type子接口类型进行解剖
1). 公共测试类和测试方法
class TT{
//Test1 ParameterizedType
public static <E> voidtestMethodI(ArrayList<ArrayList<E>> al){}
//Test2 GenericArrayType
public static <E> voidtestMethodII(ArrayList<String>[][] al){}
//Test3 WildcardType
public static <E> void testMethodIII(ArrayList<? extends Map<E, String>> al){}
//Test4 TypeVariable
public static <E extends Map<String, Date>&Cloneable &Serializable> void testMethodIV(E e){}
}
2). 通过Method解析ParameterType类型参数
(1). 测试代码
[1]. 对ParameterizedType处理的封装
public static void analyzeParameterTypeParts(StringclassName,
StringmethodName) throws ClassNotFoundException,
NoSuchMethodException{
Classclazz =Class.forName(className);
Methodmethod =clazz.getMethod(methodName, ArrayList.class);
Type[]genericParameterTypes =method.getGenericParameterTypes();
ParameterizedTypepType =null;
TyperawType =null;
Type[]actualTypeArguments =null;
for(int i=0; i<genericParameterTypes.length; i++){
//由于这个方法只有一个ParameterizedType类型的参数 所以
pType=(ParameterizedType)genericParameterTypes[i];
rawType=pType.getRawType();
actualTypeArguments=pType.getActualTypeArguments();
System.out.println("参数化类型是:"+ pType);
System.out.println("原始类型是:"+ rawType);
System.out.println("实际类型是:"+Arrays.asList(actualTypeArguments));
}
}
[2]. 测试代码
String className ="TT";
String methodName ="testMethodI";
analyzeParameterTypeParts(className, methodName);
(2). 测试结果
3). 通过Method解析GenericArrayType类型参数
(1). 测试代码
[1]. 对GenericArrayType处理的封装
public static void analyzeGenericArratTypeParts(StringclassName,
StringmethodName) throws ClassNotFoundException,
NoSuchMethodException{
Classclazz =Class.forName(className);
Methodmethod =clazz.getMethod(methodName, ArrayList[][].class);
Type[]genericParameterTypes =method.getGenericParameterTypes();
GenericArrayTypegType =null;
TypecomponentType =null;
for(int i=0; i<genericParameterTypes.length; i++){
//由于这个方法只有一个GenericArrayType类型的参数 所以
gType=(GenericArrayType)genericParameterTypes[i];
componentType=gType.getGenericComponentType();
System.out.println("泛型数组类型是:"+ gType);
System.out.println("泛型数组元素类型是:"+componentType);
}
}
[2]. main方法测试
String className ="TT";
String methodName ="testMethodII";
analyzeGenericArratTypeParts(className, methodName);
(2). 测试结果
4). 通过Method解析WildcardType类型参数
(1). 测试代码
[1]. 对WildcardType处理的封装
【注意】由于通配符表达式类型不能直接做参数,必须依附在PrameterizedType类型之后才能发挥作用。所以封装的测试方法是先解剖参数化类型,然后获取到的实际类型是WildcardType的类型,再去处理WildcardType类型的细节部分。
public static void analyzeWildcardTypeParts(StringclassName,
StringmethodName) throws ClassNotFoundException,
NoSuchMethodException{
Classclazz =Class.forName(className);
Methodmethod =clazz.getMethod(methodName, ArrayList.class);
Type[]genericParameterTypes =method.getGenericParameterTypes();
ParameterizedTypepType =null;
TyperawType =null;
Type[]actualTypeArguments =null;
WildcardTypewType =null;
Type[]upperBounds =null;
Type[]lowerBounds =null;
for(int i=0; i<genericParameterTypes.length; i++){
//由于这个方法只有一个ParameterizedType类型的参数 所以
pType=(ParameterizedType)genericParameterTypes[i];
rawType=pType.getRawType();
actualTypeArguments=pType.getActualTypeArguments();
System.out.println("参数化类型是:"+ pType);
System.out.println("原始类型是:"+ rawType);
System.out.println("实际类型是:"+Arrays.asList(actualTypeArguments));
System.out.println();
System.out.println("*******************处理WildcardType类型参数*******************");
System.out.println();
//这里面---实际类型参数设置类WildcardType类型 所以
for(int j =0; j<actualTypeArguments.length; j++){
//由于这个方法只有一个WildcardType类型的参数 所以
wType=(WildcardType)actualTypeArguments[j];
lowerBounds=wType.getLowerBounds();
upperBounds=wType.getUpperBounds();
System.out.println("通配符表达式类型是:"+ wType);
if(upperBounds.length !=0)
System.out.println("表达式上边界:"+Arrays.asList(upperBounds));
if(lowerBounds.length !=0)
System.out.println("表达式上边界:"+Arrays.asList(lowerBounds));
}
}
}
[2]. main方法测试
String className ="TT";
String methodName ="testMethodIII";
analyzeWildcardTypeParts(className,methodName);
(2). 测试结果
5). 通过Method解析TypeVariable类型参数
(1). 具有多边界的类型变量出现的思考
[1]. 测试类TT中的方法IV,定义的类型变量是
public static <E extends Map<String, Date>& Cloneable&Serializable>void testMethodIV(E e){}
【分析】如果使用getMethod来获取这个方法,形参是类型变量并且定义的时候具有边界,按照前面讲过的规律,应该将这个E擦除到extends指定的父类。但是这个时候,由于制定了多个父类类型,到底指定为哪一个正确呢?
【思路】在不清楚擦除后参数的情况下,那就让系统告诉你!
[2]. 增加测试类中的测试方法
class TT{
public static <E extends Map<String, Date>&Cloneable &Serializable>void testMethodIV(E e){}
public static <E extends Cloneable &Serializable&Map<String, Date>>void testMethodV(E e){}
public static <E extends Serializable& Cloneable&Map<String, Date>>void testMethodVI(E e){}
}
[3]. 测试代码
String className ="TT";
String methodName ="testMethodIII";
Class clazz =Class.forName(className);
Method[] methods =clazz.getMethods();
for(Method method: methods){
System.out.println(method);
}
[4]. 测试结果
从上面测试结果和方法的对应关系可以看出:
【结论】具有多边界的类型变量被擦除后,擦除到书写顺序上面第一个父边界。
(2). 测试代码
[1]. 对TypeVariable处理的封装
public static void analyzeTypeVariableParts(StringclassName,
StringmethodName) throws ClassNotFoundException,
NoSuchMethodException{
Classclazz =Class.forName(className);
Methodmethod =clazz.getMethod(methodName, Map.class);
Type[]genericParameterTypes =method.getGenericParameterTypes();
TypeVariabletVariable=null;
Type[]bounds=null;
for(int i=0; i<genericParameterTypes.length; i++){
//由于这个方法只有一个TypeVariable类型的参数 所以
tVariable=(TypeVariable)genericParameterTypes[i];
bounds=tVariable.getBounds();
System.out.println("类型变量是:"+ tVariable);
System.out.println("父边界是:"+Arrays.asList(bounds));
}
}
[2]. main方法测试
String className ="TT";
String methodName ="testMethodIV";
analyzeTypeVariableParts(className, methodName);
(3). 测试结果
3. 使用递归对任意复合泛型类型进行彻底解剖
1). 上面的代码分析
(1). 局限性
只能是按照设定好类型进行强转,才能使用。
(2). 改进思路
[1]. 应该对getGenericParameterTypes()获取到的每一个Type的类型进行判断到底是哪一种子接口类型或者实现子类。
[2]. 根据不同的实现子类或者子接口采取不同的处理策略。
[3]. 有时候,由于通过[][]…[]或者<<<…>>>进行了多级排列或者多级嵌套,从而导致一次使用的方法返回的仍然是复合泛型类型,所以继续必须逐级判断,分析出每一个嵌套的类型。不管是解析到哪一级别的类型,子类型属于ParameterizedType、TypeVariable、GenericArrayType、WildcardType或者Class类型之间的一种类型。
【规律】子问题仍然重复父问题的解决办法 ------应该使用递归解决
[4]. 递归处理各个级别的类型判定和处理
{1}. 递归的出口:对Type的五个子类型,只有处理到Class类别的时候,才是最终的原始类型,没有符合类型存在。所以递归的出口就设置为类型跳到Class的时候,停止运算。
[5]. 为了显示出处理子类型位于嵌套的具体处理级别,附加了“*”纵深标记处理。
(3). 测试方法及其测试类
class ExperimentalClass{
//Test1 ParameterizedType
public static <E> voidprintColl(ArrayList<ArrayList<E>> al){}
//Test2 GenericArrayType
public static <E> void printCollII(ArrayList<String>[][]al){}
//Test3 WildcardType
public static <E> void printCollIII(ArrayList<?extends Map<E, String>> al){}
//Test4 TypeVariable
public static <E extends Map<String, Date>&Cloneable &Serializable>void printCollIV(E e){}
}
(4). 总处理方法
/**
* 处理一个方法中的参数类型 如果是复合的泛型类型 层层解剖到最后 递归处理
* ---设计成可变参数数组 这样 可以打印Type[]中含有一个成员 方便
* @param level
* @param types
*/
public static void printTypesII(int level, Type... types) {
level++;
Stringprefix =prefixGenerator(level);
for(Type type: types){
if(typeinstanceof ParameterizedType){
System.out.println(prefix +"ParameterizedType:"+ type);
tackleParameterizedType(level,type);
}else if(type instanceof GenericArrayType){
System.out.println(prefix+"GenericArrayType:"+ type);
tackleGenericArrayType(level,type);
}else if(type instanceof WildcardType){
System.out.println(prefix+"WildcardType:"+ type);
tackleWildcardType(level,type);
}else if(type instanceof TypeVariable){
System.out.println(prefix+"TypeVariable:"+ type);
tackleTypeVariable(level,type);
}else if(type instanceof Class)//出口
System.out.println(prefix +"Class...:"+ type);
else
System.out.println("Others...");
}
}
(5). 各等级处理方法
/**
* 1.处理ParameterizedType
* @param level
* @param type
*/
private static void tackleParameterizedType(int level, Type type) {
ParameterizedTypepType =(ParameterizedType)type;
//递归调用
TyperawType =pType.getRawType();
printTypesII(level,rawType);
//递归调用
Type[]actualArguments =pType.getActualTypeArguments();
printTypesII(level,actualArguments);
}
/**
* 2.处理TypeVariable
* @param level
* @param type
*/
public static void tackleTypeVariable(int level, Type type) {
TypeVariabletVariable =(TypeVariable)type;
Type[]upBounds =tVariable.getBounds();
printTypesII(level,upBounds)
}
/**
* 3.处理WildcardType
* @param level
* @param type
*/
private static void tackleWildcardType(int level, Type type) {
WildcardTypewType =(WildcardType)type;
Type[]lowerBounds =wType.getLowerBounds();
Type[]upBounds =wType.getUpperBounds();
//extends和super只能有一个!!!所以判断下 免得做无谓的计算
if(lowerBounds.length !=0)
printTypesII(level,lowerBounds);
if(upBounds.length !=0)
printTypesII(level,upBounds);
}
/**
* 4.处理GenericArrayType
* @param level
* @param type
*/
private static void tackleGenericArrayType(int level, Type type) {
GenericArrayTypegType =(GenericArrayType)type;
//递归调用
TypecomponentType =gType.getGenericComponentType();
printTypesII(level,componentType);
}
/**
* 5.产生纵深标记
* @param level
* @return
*/
private static String prefixGenerator(int level){
StringBuildersBuilder =new StringBuilder();
for(int i=0; i<level; i++)
sBuilder.append("*");
return sBuilder.toString();
}
(6). 分级测试
[1]. 包装测试类
/**
*
* @param className测试的方法所在的类名
* @param methodName测试的方法名
* @param actualParameterClasses方法名对应的实际Class参数列表
* @throws ClassNotFoundException
* @throws NoSuchMethodException
*/
public static void generalTest(String className, StringmethodName,
Class<?>[]actualParameterClasses) throwsClassNotFoundException,
NoSuchMethodException{
Classclazz =Class.forName(className);
Methodmethod =clazz.getMethod(methodName, actualParameterClasses);
Type[]types =method.getGenericParameterTypes();
int level =0;
printTypesII(level,types);
}
[2]. 测试ParameterizedType类型:
{1}. 测试代码
String className="ExperimentalClass";
String methodName ="printColl";
Class<?>[] actualParameterClasses =new Class[]{ArrayList.class};
generalTest(className, methodName, actualParameterClasses);
{2}. 测试结果
*ParameterizedType:java.util.ArrayList<java.util.ArrayList<E>>
**Class...:class java.util.ArrayList
**ParameterizedType:java.util.ArrayList<E>
***Class...:class java.util.ArrayList
***TypeVariable:E
****Class...:classjava.lang.Object
[3]. 测试GenericArrayType类型:
{1}. 测试代码
String className="ExperimentalClass";
String methodName ="printCollII";
Class<?>[] actualParameterClasses =new Class[]{ArrayList[][].class};
generalTest(className, methodName, actualParameterClasses);
{2}. 测试结果
*GenericArrayType:java.util.ArrayList<java.lang.String>[][]
**GenericArrayType:java.util.ArrayList<java.lang.String>[]
***ParameterizedType:java.util.ArrayList<java.lang.String>
****Class...:class java.util.ArrayList
****Class...:class java.lang.String
[4]. 测试WildcardType类型:
{1}. 测试代码
String className="ExperimentalClass";
String methodName ="printCollIII";
Class<?>[] actualParameterClasses =new Class[]{ArrayList.class};
generalTest(className, methodName, actualParameterClasses);
{2}. 测试结果
*ParameterizedType:java.util.ArrayList<? extends java.util.Map<E,java.lang.String>>
**Class...:class java.util.ArrayList
**WildcardType:? extends java.util.Map<E,java.lang.String>
***ParameterizedType:java.util.Map<E, java.lang.String>
****Class...:interface java.util.Map
****TypeVariable:E
*****Class...:class java.lang.Object
****Class...:class java.lang.String
[5]. 测试TypeVariable类型:
{1}. 测试代码
String className="ExperimentalClass";
String methodName ="printCollIV";
Class<?>[] actualParameterClasses =new Class[]{Map.class};
generalTest(className, methodName, actualParameterClasses);
{2}. 测试结果
*TypeVariable:E
**ParameterizedType:java.util.Map<java.lang.String, java.util.Date>
***Class...:interface java.util.Map
***Class...:class java.lang.String
***Class...:class java.util.Date
**Class...:interface java.lang.Cloneable
**Class...:interface java.io.Serializable
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------