Java中的泛型设计
Java中的
泛型类型(geneic type),它只在程序源码中存在,在编译后的字节码中就已经替换为对应的
原始类型(raw type)。既然类文件的泛型信息都已经被擦除,那JVM中就不可能存在泛型这种类型。如JVM中并不存在ArrayLis<Integer>这种类型,不管是ArrayLis<Integer>或ArrayList<String>在JVM中都是同一种类型,即原始类型ArrayList。可通过以下测试
public static void main(String[] args) {
ArrayList list = new ArrayList();
ArrayList<String> list_str = new ArrayList<String>();
ArrayList<Integer> list_int = new ArrayList<Integer>();
if(list.getClass() == ArrayList.class
&& list_str.getClass() == ArrayList.class
&& list_int.getClass() == ArrayList.class) { //true
System.out.println("它们都属于 ArrayList 类型");
}
}
类反射与Type接口
虽然JVM中并不存在泛型类型,但还是通过类反射获得部分泛型信息(为此JDK1.5之后class文件中增加了一些泛型信息描述)。为了支持泛型类型的声明,Java提供了一个接口
java.lang.reflect.Type,它的子类如下:
(1)Class类 描述原始类型(即普通类型)
(2)ParameterizedType接口 描述参数化类型,如List<String>、Map<String,Integer>等
(3)TypeVariable接口 描述类型变量类型
(4)WildcardType接口 描述通配符类型
(5)GeneicArrayType接口 描述泛型数组类型
(1)Class类 描述原始类型(即普通类型)
(2)ParameterizedType接口 描述参数化类型,如List<String>、Map<String,Integer>等
(3)TypeVariable接口 描述类型变量类型
(4)WildcardType接口 描述通配符类型
(5)GeneicArrayType接口 描述泛型数组类型
在类反射中我们可以通过Type接口及其子类获得部分泛型信息,如以下所示
public class Data<T> {
String str;
List<String> list;
T item;
public static void main(String[] args) {
Data<Double> data = new Data<Double>();
Class<?> clazz = data.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Type type = field.getGenericType();
if (type instanceof Class) {
//属性str属于原始类型
}
if (type instanceof ParameterizedType) { // 属性list
ParameterizedType pt = (ParameterizedType) type;
//返回参数类型对应的原始类型,这是为 java.util.List
Type rawType = pt.getRawType();
//返回泛型参数数组,这里为 [java.lang.String]
Type[] typeArgs = pt.getActualTypeArguments();
}
if (type instanceof TypeVariable) { // 属性item
TypeVariable<?> tv = (TypeVariable<?>) type;
//返回该变量类型的名字,这里为T
String name = tv.getName();
}
}
}
}
获取Class对象中的泛型信息
上面我已经提过泛型类型在生成class文件的时候就已经被擦除,而Class对象作为class文件在JVM中的表现形式,究竟能获得什么样的泛型信息呢?比如在上面的例子中我通过Data<Double> data = new Data<Double>()实例化了Data<T>类,那通过对象data来获取它实例化时的泛型参数(即Double)吗?毫无疑问要通过类反射获得,即通过该对象对应的Class对象获得。
当时我就是顺着这个思路搞了很久才发现走不通。很明显,Class对象是和该Class对应的任意实例无关的,它存的是定义它的class文件中的信息。于是结论便是不可能通过类反射获取到data对象的实例化参数。
不过,我们还是可以看看Class类的API文档总返回Type类型的接口有哪些:
(1)Type[] getGenericInterfaces() 返回该类继承的接口对应的类型
(2)Type getGenericSuperclass() 返回该类的父类对应的类型
通过以上两个接口我们可以知道通过Class可以获取该父类或接口的泛型类型,如以下代码所示:
public class Data<T> {
String str;
List<String> list;
T item;
public static void main(String[] args) {
//定义了一个匿名内部类并实例化
Data<Double> data = new Data<Double>(){
};
Class<?> clazz = data.getClass();
Type type = clazz.getGenericSuperclass();
if(type instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType)type;
//返回参数类型对应的原始类型,这是为 xx.xx.Data
Type rawType = pt.getRawType();
//返回泛型参数数组,这里为 [java.lang.Double]
Type[] typeArgs = pt.getActualTypeArguments();
}
}
}
尽管通过以上方式我们能获取到对象data的泛型参数Double,但却不得不定义一个明确泛型参数的子类,归根到底还是在于Class对象代表的是class文件,和它实例的对象无关。