1. 反射与泛型的关系是
- 泛型参数在运行时会被删除
- 但在类信息
Class
中依然有关于泛型的一些信息,可以通过反射得到
2. 反射中可以获取与泛型信息的方法有
Class
:public TypeVariable<Class<T>>[] getTypeParameters() //获取类的泛型参数信息
Field
:public Type getGenericType()
Method
public Type getGenericReturnType()
public Type[] getGenericParameterTypes()
public Type[] getGenericExceptionTypes()
Constructor
:public Type[] getGenericParameterTypes()
Type
是一个接口,Class
实现了Type
。Type
的其他子接口还有TypeVariable
:类型参数,可以有上界,比如:T extends Number
ParameterizedType
:参数化的类型,有原始类型和具体的类型参数,比如:List<String>
WildcardType
:通配符类型,比如:?
、? extends Number
、? super Integer
3. 写一个通过反射获取泛型信息的 Demo
Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32public class GenericDemo {
static class GenericTest<U extends Comparable<U>, V> {
U u;
V v;
List<String> list;
public U test(List<? extends Number> number) {
return null;
}
}
public static void main(String[] args) throws Exception {
Class<?> cls = GenericTest.class;
//类的类型参数
for(TypeVariable t : cls.getTypeParameters()) {
System.out.println(t.getName() + " extends " + Arrays.toString(t.getBounds()));
}
//字段:泛型类型
Field fu = cls.getDeclaredField("u");
System.out.println(fu.getGenericType());
//字段:参数化的类型
Field flist = cls.getDeclaredField("list");
Type listType = flist.getGenericType();
if(listType instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) listType;
System.out.println("raw type: " + pType.getRawType() + ", type arguments: " + Arrays.toString(pType.getActualTypedArguments()));
}
//方法的泛型参数
Method m = cls.getMethod("test", new Class[] { List.class });
for(Type t : m.getGenericParameterTypes()) {
System.out.println(t);
}
}
}输出
1
2
3
4
5U extends [java.lang.Comparable<U>]
V extends [class java.lang.Object]
U
raw type: interface java.util.List, tye arguments: [class java.lang.String]
java.util.List<? extends java.lang.Number>
4. 反射虽然是灵活的,但一般情况下,并不是我们优先建议的,为什么
- 反射更容易出现运行时错误。使用显示的类和接口,编译器能帮我们做类型检查,减少错误;但使用反射,类型是运行时才知道的,编译器无能为力
- 反射的性能要低一些,在访问字段、调用方法前,反射先要查找对应的
Field/Method
,要慢一些 - 简单地说,如果能用接口实现同样的灵活性,就不要使用反射