前言
Java是面向对象的,我们定义的每一个类都有一个类型,而这个类型就是java.lang.Class
对象。那么java.lang.reflect.Type
这个类呢?它又是代表什么?与java.lang.Class又有什么关系呢?
java.lang.Class
说到java.lang.Class
这个类,你会不会与class这个关键词混淆?这个类与我们平常创建的类比如说java.util.ArrayList
有什么关联吗?我们不妨先从这个类的官方注释看一下
Class类实例
代表一个运行中程序的类和接口。一个枚举(enum)是一种类,一个注解(annotation)是一种接口。
比如创建如下类、接口、枚举和注解
package com.example.type;
public interface UserService {
}
package com.example.type;
public @interface SexAnno {
String value();
}
package com.example.type;
public class User {
}
package com.example.type;
public enum SexEnum {
MAN, WOMEN;
}
然后我们创建一个测试
package com.example.type;
public class TestMain {
public static void main(String[] args) {
User user1 = new User();
User user2 = new User();
System.out.println(user1.getClass());
System.out.println(user2.getClass());
System.out.println(User.class.getClass());
System.out.println(SexAnno.class.getClass());
System.out.println(SexEnum.class.getClass());
System.out.println(UserService.class.getClass());
}
}
执行结果如下
class com.example.type.User
class com.example.type.User
class java.lang.Class
class java.lang.Class
class java.lang.Class
class java.lang.Class
这里想说明什么情况呢?这里的user1和user2是通过User创建的,我们称之为User类的两个实例,对应的实例通过getClass获取到它们的类型,在这里当然就是User了。但是User这个类呢?它其实也是其他类的对象,而这个类就是java.lang.Class
,不但它是,其他我们创建的任何类、接口、枚举、注解也都是它的实例。之所以我们不会通过Class User = new Class()
这么做是因为这些工作是由虚拟机来完成的。
每个数组还属于一个反映为Class对象的类,该对象由具有相同元素类型和维数的所有数组共享。这是啥意思呢?我们再看一下以下这个案例
User[] userArr1 = new User[3];
User[] userArr2 = new User[4];
System.out.println(userArr1.getClass());
System.out.println(userArr2.getClass());
System.out.println(userArr1.getClass() == userArr2.getClass());
测试结果如下
class [Lcom.example.type.User;
class [Lcom.example.type.User;
true
虽然数组userArr1和userArr2声明的大小不一样,但是它们仍然属于同一个类User[].class
,只与元素类型和维数(这里为1维)有关系。另外,原始Java类型(boolean,byte,char,short,int,long,float和double)以及关键字void也都表示为Class对象。其实Class类是没有公开的构造器的,取而代之的是,Java虚拟机会在加载类时以及通过在类加载器中调用defineClass方法来自动构造Class对象。通过方法java.lang.ClassLoader#defineClass(byte[], int, int)
可以加载二进制创建一个Class对象。注意这里不是java文件,而是class文件。一般从java文件编译成class文件是由编译器通过javac帮我们完成的。比如在指定目录下放入一个Java文件
package com.example.type;
public class User {
private String name = "I am Iron Man";
public String getName() {
return name;
}
}
通过java编译文件,然后读取编译好的字节码文件为二进制,通过ClassLoader读取成Class对象。
package com.example.type;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMain {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
1. 将java文件编译成字节码文件
String javaFile = "D:\\app\\type\\User.java";
JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
// 编译成功 对应的字节码文件会生成
int compilationResult = systemJavaCompiler.run(null, null, null, javaFile);
if (compilationResult != 0) {
System.out.println("没有编译成功!");
}
String classFile = "D:\\app\\type\\User.class";
2. 自定义一个类加载器专门用于读取User.class字节码文件
ClassLoader myClassLoader = new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(new File(classFile));
int available = inputStream.available();
byte[] bytes = new byte[available];
inputStream.read(bytes);
// 根据读取的字节创建一个Class对象 这里是写死了 仅做模拟
return defineClass(null, bytes, 0, available);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return super.findClass(name);
}
};
3. 通过自定义的类加载器读取字节码构造Class对象
Class<?> aClass = myClassLoader.loadClass("com.example.type.User");
4. 通过类对象反射创建一个类实体对象
Object instance = aClass.newInstance();
5. 反射获取目标方法
Method method = aClass.getMethod("getName");
6. 反射调用实例方法
Object invoke = method.invoke(instance);
7. 打印方法调用结果
System.out.println(invoke);
}
}
运行的结果如下:
通过以上的几个小案例,我们可以得出结论:我们创建的每个类其实都是Class的实例对象,java文件通过编译器编译之后生成class字节码文件,然后虚拟器启动的过程中,通过类加载器加载字节码文件并实例化成Class具体实例,当然还包括初始化这个具体实例(类的静态块)。
java.lang.reflect.Type
对于java.lang.Class
,其实理解并不是很难,因为通过java.lang.Object#getClass这个接口就可以获取任何对象所属的类,但是java.lang.reflect.Type
呢?却没有这么方便了。看看这两个类的包名,也可以看出Type更多与反射关联在一起的,再看看这个类的定义。
相比java.lang.Class
在1.0时代就有了,而java.lang.reflect.Type
直到1.5版本才加入Java。从这个类的注释来看:Type是Java编程语言中所有类型的通用超级接口。 这些包括原始类型,参数化类型,数组类型,类型变量和基本类型。它们二者有没有关系呢?
原来java.lang.Class
仅仅是java.lang.reflect.Type
的一种实现而已。从这里我们看到,除了java.lang.Class
之外,还有很多其他的子类型,比如java.lang.reflect.ParameterizedType
、java.lang.reflect.WildcardType
、java.lang.reflect.GenericArrayType
和java.lang.reflect.TypeVariable
。看到这么几个Type,感觉一下子复杂起来了。假设现在有如下这样一个接口
package com.example.type;
import java.util.List;
import java.util.Map;
public interface Level0Mapper<L, M, N> {
void simpleSelectVoid(Integer param);
double simpleSelectPrimitive(int param);
Double simpleSelect();
List<Double> simpleSelectList();
Map<Integer, Double> simpleSelectMap();
String[] simpleSelectArray();
String[][] simpleSelectArrayOfArray();
List<? extends String> simpleSelectWildcard();
N select(N param);
List<N> selectList(M param1, N param2);
List<? extends N> selectWildcardList();
Map<N, M> selectMap();
N[] selectArray(List<N>[] param);
N[][] selectArrayOfArray();
List<N>[] selectArrayOfList();
}
首先测试几个简单的
package com.example.type;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
public class TestMain {
public static void main(String[] args) throws NoSuchMethodException {
Method simpleSelect = Level0Mapper.class.getMethod("simpleSelect");
// 查看返回参数类型
Class<?> returnType = simpleSelect.getReturnType();
// 查看泛型返回类型
Type genericReturnType = simpleSelect.getGenericReturnType();
System.out.println("returnType = " + returnType + " , genericReturnType = " + genericReturnType);
Method simpleSelectList = Level0Mapper.class.getMethod("simpleSelectList");
returnType = simpleSelectList.getReturnType();
genericReturnType = simpleSelectList.getGenericReturnType();
System.out.println("returnType = " + returnType + " , genericReturnType = " + genericReturnType);
Method simpleSelectMap = Level0Mapper.class.getMethod("simpleSelectMap");
returnType = simpleSelectMap.getReturnType();
genericReturnType = simpleSelectMap.getGenericReturnType();
System.out.println("returnType = " + returnType + " , genericReturnType = " + genericReturnType);
Method simpleSelectArray = Level0Mapper.class.getMethod("simpleSelectArray");
returnType = simpleSelectArray.getReturnType();
genericReturnType = simpleSelectArray.getGenericReturnType();
System.out.println("returnType = " + returnType + " , genericReturnType = " + genericReturnType);
}
}
这是一个非常简单的方法,通过方法可以获取返回参数的类型以及泛型类型,所以可以看出,其实对于Class我们认为是普通类型,而Type更多认为是泛型类型,如果不涉及到复杂的泛型,那么一个类其实的泛型类型就是普通类型。此时控制台输出如下
returnType = class java.lang.Double , genericReturnType = class java.lang.Double
java.lang.Double是一个很简单的类型,那么对于List<Double>
呢?此时returnType和genericReturnType还是一样的吗?
这个时候我们发现,returnType和genericReturnType不是一样的,首先前者是Class类型的,而后者是org.apache.ibatis.reflection.TypeParameterResolver.ParameterizedTypeImpl
类型的,而这个类又是上面我们提到的java.lang.reflect.ParameterizedType
接口的实现类。这个接口提供了如下几个方法
其中通过getRawType返回的rawType竟然和returnType是同一个Class对象(java.util.List)。另外在getActualTypeArguments返回的结果是一个数组,其中第一个元素就是我们的List的真实类型( java.lang.Double)。
控制台打印如下
returnType = interface java.util.List , genericReturnType = java.util.List<java.lang.Double>
我们再看看一下java.lang.reflect.ParameterizedType
的注释:ParameterizedType represents a parameterized type such as Collection<String>.
A parameterized type is created the first time it is needed by a reflective method, as specified in this package. When a parameterized type p is created, the generic type declaration that p instantiates is resolved, and all type arguments of p are created recursively. See TypeVariable for details on the creation process for type variables. Repeated creation of a parameterized type has no effect.
Instances of classes that implement this interface must implement an equals() method that equates any two instances that share the same generic type declaration and have equal type parameters
从以上注释中我们可以得出结论:ParameterizedType用于表示类似Collection<String>的参数化类型,在调用相关的反射方法的第一次时创建,此时会解析出泛型类型以及所有的参数类型。
我们再继续看下一个例子,方便对ParameterizedType的理解。
此时的rawType与returnType是一致的,都是Class对象(java.util.Map),而实际参数类型数组长度为2,分别为两个Class对象(java.lang.Integer+java.lang.Double)。对应控制台输出如下
returnType = interface java.util.Map , genericReturnType = java.util.Map<java.lang.Integer, java.lang.Double>
所以:ParameterizedType = RawType对应的Type + ActualTypeArguments对应的Type[],在上面的案例当中,RawType对应的Type恰好是一个Class对象,而ActualTypeArguments对应的数组元素也都是Class对象,但是实际中可能会是其他的Type类型。
在很多情况下,我们会将List与数组等同,尤其是ArrayList。但是这二者从Type上来看是完全不同的。比如说List<String>和String[],前者属于ParameterizedType类型,后者是Class类型。为啥呢?其实泛型是从Java1.5开始的,而数组从Java1.0就有了。可以直接通过java.lang.Class#isArray方法判断一个类是不是数组。
如果是属于数组的话,也可以获取数组元素的实际类型。通过java.lang.Class#getComponentType方法就可以获取了。
List<String>在java.lang.reflect.Type体系当中是属于java.lang.reflect.ParameterizedType,而String[]是属于java.lang.Class的,获取对应的实际元素类型前者是通过java.lang.reflect.ParameterizedType#getActualTypeArguments方法,而后者是通过java.lang.Class#getComponentType方法。
再看一下以下这个方法
List<? extends String> simpleSelectWildcard();
Method simpleSelectWildcard = Level0Mapper.class.getMethod("simpleSelectWildcard");
// 查看返回参数类型
Class<?> returnType = simpleSelectWildcard.getReturnType();
// 查看泛型返回类型
Type genericReturnType = simpleSelectWildcard.getGenericReturnType();
System.out.println("returnType = " + returnType + " , genericReturnType = " + genericReturnType);
genericReturnType仍然为ParameterizedType,对应的rawType与returnType相同而且为Class类型,与我们上面没啥差别。只是这里的实际参数类型数组元素变了,个数仍然是一个,类型为org.apache.ibatis.reflection.TypeParameterResolver.WildcardTypeImpl
,这个是java.lang.reflect.WildcardType
类型的实现类。这个类的注解如下
这个类型我们可以称之为通配符类型,对应的方法有两个,与java.lang.reflect.ParameterizedType#getActualTypeArguments方法相同的是,getUpperBounds和getLowerBounds的返回类型也是Type[],那什么是上限(UpperBounds)?什么是下限(LowerBounds)呢?比如在上面注释中,? extends Number
,那么这个?
可能为Number以及Number的子类,但绝不能是Number的父类,所以这个Number也就成了这个?
的上限,同样? super Integer,满足条件的只能是Integer自己或者父类,而不能是子类,所以此时Integer也就成了?
的下限。也就是说这里的上限和下限是从类继承结构上来说的。比如在上面的例子当中,? extends String
就限定了这个通配符的上限为String。
以上的测试结果为
returnType = interface java.util.List , genericReturnType = java.util.List<? extends java.lang.String>
List<? extends String>
是一个ParameterizedType = Class + WildcardType。
继续看下面这例子,这里我们将接口中的N进行了上限限定(N extends Comparable
)
public interface Level0Mapper<L, M, N extends Comparable> {
List<N> selectList(M param1, N param2);
}
1. 反射获取方法 方法名称为selectList 对应的参数个数为2个 类型为Object Object
Method selectList = Level0Mapper.class.getMethod("selectList", Object.class, Object.class);
returnType = selectList.getReturnType();
genericReturnType = selectList.getGenericReturnType();
System.out.println("returnType = " + returnType + " , genericReturnType = " + genericReturnType);
此时会抛出如下异常
Exception in thread "main" java.lang.NoSuchMethodException: com.example.type.Level0Mapper.selectList(java.lang.Object, java.lang.Object)
at java.lang.Class.getMethod(Class.java:1786)
at com.example.type.TestMain.main(TestMain.java:17)
然后我们将参数类型改一下
2. 反射获取方法 方法名称为selectList 对应的参数个数为2个 类型为Object Comparable
3. 与上面的例子相比 这里将第二个参数类型改为了Comparable
Method selectList = Level0Mapper.class.getMethod("selectList", Object.class, Comparable.class);
returnType = selectList.getReturnType();
genericReturnType = selectList.getGenericReturnType();
System.out.println("returnType = " + returnType + " , genericReturnType = " + genericReturnType);
此时程序没有报错,输出的结果如下
returnType = interface java.util.List , genericReturnType = java.util.List<N>
为什么这里第二个参数类型改为Comparable就不会报错,而使用Object就会报错呢,其实这里就是上限与下限的问题,在上面我们故意将接口中的N extends Comparable,这样就是N的上限包括Comparable,而在继承结构上java.lang.Object是java.lang.Comparable的父类,超过了上限,这里就找不到对应的方法了。这里仅仅是针对上限和下限的一个举例,我们还是看一下这个方法的返回类型。
在这里返回类型为List,与List<String>以及List<? extends String>一样都是ParameterizedType,但是通过getActualTypeArguments返回的Type数组是不同的,这里为java.lang.reflect.TypeVariable接口类型(sun.reflect.generics.reflectiveObjects.TypeVariableImpl为它的实现类)。
TypeVariable这里也有一个与Bounds有关的getBounds方法,那么它与通配符类型(java.lang.reflect.WildcardType)有什么区别吗?在泛型当中,?可以代表所有的类型,而其他字母,比如T、M、N、K只能代表一种类型。这个确定的类型一般在子类中进行限定。比如看一下以下这两个方法
在第一个方法中可以传入两种类型的List,完全没问题,而第二个就直接编译都通不过了,因为T不能既是java.lang.Integer,又是java.lang.Long。所以TypeVariable只有getBounds方法,而不是什么上限和下限。
扩展
通过以上的几个简单示例我们介绍了Type,以及它的几个实现类,java.lang.reflect.ParameterizedType、java.lang.reflect.WildcardType、java.lang.reflect.TypeVariable,当然还有很重要的java.lang.Class。对于这些基础知识我们需要理解,但是在实际工作中还是需要一些工具类来帮忙解决的。在MyBatis中就提供了这样的工具类帮助我们解析一个方法的参数类型和返回值类型。
package org.apache.ibatis.reflection;
import java.lang.reflect.*;
import java.util.Arrays;
/**
* @author Iwao AVE!
*/
public class TypeParameterResolver {
private TypeParameterResolver() {
super();
}
/**
* @return The field type as {@link Type}. If it has type parameters in the declaration,<br>
* they will be resolved to the actual runtime {@link Type}s.
*/
public static Type resolveFieldType(Field field, Type srcType) {
Type fieldType = field.getGenericType();
Class<?> declaringClass = field.getDeclaringClass();
return resolveType(fieldType, srcType, declaringClass);
}
/**
* @param method 方法
* @param srcType 方法所在的类
* @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
* they will be resolved to the actual runtime {@link Type}s.
*/
public static Type resolveReturnType(Method method, Type srcType) {
Type returnType = method.getGenericReturnType();
Class<?> declaringClass = method.getDeclaringClass();
return resolveType(returnType, srcType, declaringClass);
}
/**
* @return The parameter types of the method as an array of {@link Type}s. If they have type parameters in the declaration,<br>
* they will be resolved to the actual runtime {@link Type}s. method为指定的方法 而srcType为方法所在的类的类型
*/
public static Type[] resolveParamTypes(Method method, Type srcType) {
Type[] paramTypes = method.getGenericParameterTypes();
// 方法生命类型与srcType可能不一样 如果是在父类中定义的方法 此处将返回父类
Class<?> declaringClass = method.getDeclaringClass();
Type[] result = new Type[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
result[i] = resolveType(paramTypes[i], srcType, declaringClass);
}
return result;
}
}
在源码中还包含了丰富的测试案例,对应的案例接口如下
package org.apache.ibatis.reflection.typeparam;
public class Calculator<T> {
protected T id;
protected T attribute;
private T fld;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
public static class SubCalculator extends Calculator<String> {
}
}
package org.apache.ibatis.reflection.typeparam;
import java.util.List;
import java.util.Map;
public interface Level0Mapper<L, M, N> {
void simpleSelectVoid(Integer param);
double simpleSelectPrimitive(int param);
Double simpleSelect();
List<Double> simpleSelectList();
Map<Integer, Double> simpleSelectMap();
String[] simpleSelectArray();
String[][] simpleSelectArrayOfArray();
<K extends Calculator<?>> K simpleSelectTypeVar();
List<? extends String> simpleSelectWildcard();
N select(N param);
List<N> selectList(M param1, N param2);
List<? extends N> selectWildcardList();
Map<N, M> selectMap();
N[] selectArray(List<N>[] param);
N[][] selectArrayOfArray();
List<N>[] selectArrayOfList();
Calculator<N> selectCalculator(Calculator<N> param);
List<Calculator<L>> selectCalculatorList();
interface Level0InnerMapper extends Level0Mapper<String, Long, Float> {
}
}
package org.apache.ibatis.reflection.typeparam;
public interface Level1Mapper<E, F> extends Level0Mapper<E, F, String> {
}
package org.apache.ibatis.reflection.typeparam;
import java.io.Serializable;
import java.util.Date;
public interface Level2Mapper extends Level1Mapper<Date, Integer>, Serializable, Comparable<Integer>{
}
比如以下的测试中,在Level1Mapper中已经定义了N为String类型,那么select这个方法的参数类型和返回类型应该都是Class类型对象String。
/**
* N select(N param) 这个N在子类中限定为String
* 方法是在Level0Mapper<L, M, N>中定义的
* 当前接口 Level1Mapper<E, F> extends Level0Mapper<E, F, String>
*/
@Test
public void testReturn_Lv1Class() throws Exception {
Class<?> clazz = Level1Mapper.class;
// 可以通过参数类型Object获取到方法
Method method = clazz.getMethod("select", Object.class);
Type result = TypeParameterResolver.resolveReturnType(method, clazz);
assertEquals(String.class, result);
}
而接下来的org.apache.ibatis.reflection.TypeParameterResolver#resolveTypeVar方法就稍微复杂了
private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
Type result = null;
Class<?> clazz = null;
1. 首先这里要看srcType的类型(外部传入的) srcType指的是当前方法所在的类 Level1Mapper.class
2. 但是要跟declaringClass区分开 declaringClass指的是方法声明的类
3. 在当前测试中srcType指的是Level1Mapper.class,而declaringClass指的是Level0Mapper<L, M, N>
4. Level1Mapper<E, F> extends Level0Mapper<E, F, String>
5. 之所以这里要关联srcType与declaringClass,就是为了得出N为String
if (srcType instanceof Class) {
6. 外部传入 此处只能是Class类型或者ParameterizedType类型
clazz = (Class<?>) srcType;
} else if (srcType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) srcType;
clazz = (Class<?>) parameterizedType.getRawType();
} else {
throw new IllegalArgumentException(
"The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
}
7. 当前类型是否与声明类型相同 一般方法可能是在父类或者接口中声明的 此处就不是相同的
if (clazz == declaringClass) {
Type[] bounds = typeVar.getBounds();
if (bounds.length > 0) {
return bounds[0];
}
return Object.class;
}
8. 遍历泛型父类查找关系 此处没有泛型父类
Type superclass = clazz.getGenericSuperclass();
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
if (result != null) {
return result;
}
9. 遍历泛型接口查找关系
Type[] superInterfaces = clazz.getGenericInterfaces();
for (Type superInterface : superInterfaces) {
9.1 对应泛型接口 org.apache.ibatis.reflection.typeparam.Level0Mapper<E, F, java.lang.String>
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
if (result != null) {
return result;
}
}
return Object.class;
}
继续从泛型接口或泛型父类中查找N了,当前查找的泛型父类为org.apache.ibatis.reflection.typeparam.Level0Mapper<E, F, java.lang.String>,这是一个ParameterizedType类型的,其中rawType为Level0Mapper
private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz,
Type superclass) {
Type result = null;
1. org.apache.ibatis.reflection.typeparam.Level0Mapper<E, F, java.lang.String>是ParameterizedType类型的
if (superclass instanceof ParameterizedType) {
这里的superClass是从子类中获取到的 所以子类为中心
ParameterizedType parentAsType = (ParameterizedType) superclass;
获取rawType
Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
2. 而getDeclaringClass也是返回的Class类型,与rawType都是Level0Mapper.class,这里匹配上了,相当于找到了组织
if (declaringClass == parentAsClass) {
3. 获取当前实际参数类型 L M String
Type[] typeArgs = parentAsType.getActualTypeArguments();
4. 获取声明参数类型 比如 L M N
TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters();
5. 遍历声名的类型 这里都是TypeVariable,当声明的TypeVariable与我们当前方法中的变量是同一个的时候 说明匹配上了
for (int i = 0; i < declaredTypeVars.length; i++) {
// typeVar切好与声明参数类型是同一个 比如N 此时的N对于子类来说其实是泛型 parameterizedType.getActualTypeArguments()
6.1 声明类型数组中与目标类型匹配上了
if (declaredTypeVars[i] == typeVar) {
6.2 如果匹配的实际类型为变量类型 递归解析
if (typeArgs[i] instanceof TypeVariable) {
TypeVariable<?>[] typeParams = clazz.getTypeParameters();
for (int j = 0; j < typeParams.length; j++) {
if (typeParams[j] == typeArgs[i]) {
if (srcType instanceof ParameterizedType) {
result = ((ParameterizedType) srcType).getActualTypeArguments()[j];
}
break;
}
}
} else {
6.2 为Class类型
result = typeArgs[i];
}
}
}
} else if (declaringClass.isAssignableFrom(parentAsClass)) {
result = resolveTypeVar(typeVar, parentAsType, declaringClass);
}
} else if (superclass instanceof Class) {
if (declaringClass.isAssignableFrom((Class<?>) superclass)) {
result = resolveTypeVar(typeVar, superclass, declaringClass);
}
}
return result;
}
总结
java.lang.reflect.Type是在1.5中开始引入的,主要是因为1.5中开始引入了泛型,而1.0提供的java.lang.Class
不能够满足需求了,所以引入了其他的类型,Class能完全满足的除了原始类、数组、多维数组,其他的就没啥办法了。比如List<Double>,除了List之外,还有Double的信息需要承载,所以引入了ParameterizedType
,其中包含了RawType(List)和ActualTypeArguments(Double),同理,Map<Integer, Double>中包含了RawType(Map)和ActualTypeArguments(Integer,Double)。
/**
* Map<Integer, Double> simpleSelectMap()
*/
@Test
public void testReturn_SimpleMap() throws Exception {
Class<?> clazz = Level1Mapper.class;
Method method = clazz.getMethod("simpleSelectMap");
Type result = TypeParameterResolver.resolveReturnType(method, clazz);
assertTrue(result instanceof ParameterizedType);
ParameterizedType paramType = (ParameterizedType) result;
assertEquals(Map.class, paramType.getRawType());
assertEquals(2, paramType.getActualTypeArguments().length);
assertEquals(Integer.class, paramType.getActualTypeArguments()[0]);
assertEquals(Double.class, paramType.getActualTypeArguments()[1]);
}
对于? extends String,通过Class
和ParameterizedType
都无法满足描述完整的类型,因为包含了所谓的上限的问题,所以又有一个新的类型WildcardType
,这个类型里面又承载了UpperBounds
和LowerBounds
。如下所示
/**
* List<? extends String> simpleSelectWildcard()
*/
@Test
public void testReturn_SimpleWildcard() throws Exception {
Class<?> clazz = Level1Mapper.class;
Method method = clazz.getMethod("simpleSelectWildcard");
Type result = TypeParameterResolver.resolveReturnType(method, clazz);
assertTrue(result instanceof ParameterizedType);
ParameterizedType paramType = (ParameterizedType) result;
assertEquals(List.class, paramType.getRawType());
assertEquals(1, paramType.getActualTypeArguments().length);
assertTrue(paramType.getActualTypeArguments()[0] instanceof WildcardType);
WildcardType wildcard = (WildcardType) paramType.getActualTypeArguments()[0];
assertEquals(String.class, wildcard.getUpperBounds()[0]);
}
即便是数组,一旦面对泛型时,Class同样乏力了,比如List<N>[],所以此时又有一个java.lang.reflect.GenericArrayType类型出现了。如下所示
/**
* List<N>[] selectArrayOfList();
* <p>
* Level1Mapper<E, F> extends Level0Mapper<E, F, String>
* public interface Level2Mapper extends Level1Mapper<Date, Integer>, Serializable, Comparable<Integer>
* E = Date
* F = Integer
* N = String
*/
@Test
public void testReturn_Lv2ArrayOfList() throws Exception {
Class<?> clazz = Level2Mapper.class;
Method method = clazz.getMethod("selectArrayOfList");
Type result = TypeParameterResolver.resolveReturnType(method, clazz);
assertTrue(result instanceof GenericArrayType);
GenericArrayType genericArrayType = (GenericArrayType) result;
// 数组的基础元素为ParameterizedType 或者 TypeVariable
assertTrue(genericArrayType.getGenericComponentType() instanceof ParameterizedType);
ParameterizedType paramType = (ParameterizedType) genericArrayType.getGenericComponentType();
assertEquals(List.class, paramType.getRawType());
assertEquals(String.class, paramType.getActualTypeArguments()[0]);
}
除此之外就是java.lang.reflect.TypeVariable类型了。
导致事情变得复杂倒不是有这么些类型,而是这些类型的相互嵌套,比如ParameterizedType中getActualTypeArguments返回的也是java.lang.reflect.Type类型,多嵌套几层,通过代码来获取实际类型将会很考验编程能力了。此时我们就可以感受到MyBatis中对应工具类org.apache.ibatis.reflection.TypeParameterResolver
的好处了。实际工作中,即使不是这么复杂的场景,我们也能从对应工具类中学习java.lang.reflect.Type的使用方法。