我经常在文章或论坛中读到所有的Java范型信息都会在编译时搽除,所以不能在运行时访问范型的任何信息。这并不完全准确。某些案例下可以在运行时访问范型信息,这些案例实际上覆盖了我们对范型信息的一些需求。本文将解释这些案例。
范型反射的使用法则
使用范型通常有2种不同的场景:
- 定义一个可参数化的类/接口
- 使用可参数化的类
当你编写一个类/接口时,可以指定类/接口是否被参数化。这是java.util.List
接口的示例。你可以参数化java.util.List
接口去创建一个String类型的list,而不是object list,如下:
List myList = new ArrayList();
当使用反射检视可参数化类型本身时,比如java.util.List
,是没有办法得到它的已参数化的类型的。object自身是不知道它参数化的类型的。
然而,object的reference
知道它包含的范型引用的类型信息。那就是,如果它不是局部变量。如果一个object被另一个object的field
引用,你就可以通过反射查看Field
定义,通过field
获得范型的类型信息。
同理,如果一个object被一个方法的参数引用。可以通过方法的Parameter
,你可以看到方法参数确定范型的类型信息。
最后,你可以通过方法返回值确定反射范型信息。需要强调的是,实际返回object时你不能得到范型信息。你需要通过反射查看方法定义,去获得方法定义的返回值(包含范型信息)。
总结起来就是:你只能通过object的引用查看它的范型信息,而不能通过它自身查看。
下面的部分将仔细研究这些情况。
方法返回范型类型
如你已获得java.lang.reflect.Method
对象,那么就可以获得它的返回值的范型。你可以阅读 "Java Generics: Methods"学习如何获得Method
对象。下面示例类的方法拥有一个可参数化的返回类型。
public class MyClass {
protected List stringList = ...;
public List getStringList(){
return this.stringList;
}
}
在这个类中可以获得方法getStringList()
的带范型的返回类型。换言之,可以发现方法getStringList()
返回List
并不仅仅是List
。代码如下:
Method method = MyClass.class.getMethod("getStringList", null);
Type returnType = method.getGenericReturnType();
if(returnType instanceof ParameterizedType){
ParameterizedType type = (ParameterizedType) returnType;
Type[] typeArguments = type.getActualTypeArguments();
for(Type typeArgument : typeArguments){
Class typeArgClass = (Class) typeArgument;
System.out.println("typeArgClass = " + typeArgClass);
}
}
上面的代码块将输出"typeArgClass = java.lang.String"。Type[]
数组typeArguments
包含单个元素-java.lang.String
类的Class
实例。Class
实现了Type
接口。
方法参数是范型类型
你也可以在运行时通过反射访问方法的范型参数。下面示例类有一个方法拥有一个参数化List
作为参数:
public class MyClass {
protected List stringList = ...;
public void setStringList(List list){
this.stringList = list;
}
}
可以访问方法参数的范型类型,代码如下:
method = Myclass.class.getMethod("setStringList", List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for(Type genericParameterType : genericParameterTypes){
if(genericParameterType instanceof ParameterizedType){
ParameterizedType aType = (ParameterizedType) genericParameterType;
Type[] parameterArgTypes = aType.getActualTypeArguments();
for(Type parameterArgType : parameterArgTypes){
Class parameterArgClass = (Class) parameterArgType;
System.out.println("parameterArgClass = " + parameterArgClass);
}
}
}
上述代码将输出"parameterArgType = java.lang.String"。数组Type[]
`parameterArgTypes包含单个元素-代表类
java.lang.String的
Class实例。
Class实现了
Type`接口。
属性是范型类型
同样的,我们也可以访问公共属性的范型类型。属性是类的成员变量-无论静态或实例变量。你可以获得关于Field
的文章"Java Generics: Fields",下方是早些时候的示例代码,拥有一个名为stringList
的属性:
public class MyClass {
public List stringList = ...;
}
Field field = MyClass.class.getField("stringList");
Type genericFieldType = field.getGenericType();
if(genericFieldType instanceof ParameterizedType){
ParameterizedType aType = (ParameterizedType) genericFieldType;
Type[] fieldArgTypes = aType.getActualTypeArguments();
for(Type fieldArgType : fieldArgTypes){
Class fieldArgClass = (Class) fieldArgType;
System.out.println("fieldArgClass = " + fieldArgClass);
}
}
上面的代码将输出"fieldArgClass = java.lang.String"。Type[]
数组fieldArgTypes
包含的单元素-代表java.lang.String
类的Class
实例。Class
实现了Type
接口。