Java泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,它与C++ 中的模板(Templates)比较类似,但是有一点不同的是:Java的泛型在编译期有效,在运行 期被删除,也就是说所有的泛型参数类型在编译后都会被清除掉,我们来看一个例子,代码如下:
public class Foo {
public void arrayMethod(String[] strArray){
}
public void arrayMethod(Integer[] intArray){
}
public void listMethod(List<String> strlist){
}
public void listMethod(List<Integer> intlist){
}
}
程序很简单,编写了4个方法,arrayMethod方法接收String数组和Integer数组,这是 一个典型的重载,listMethod接收元素类型为String和Integer的List变量。现在的问题是,
这段程序是否能编译?如果不能,问题出在什么地方?
事实上,这段程序是无法编译的,编译时报错信息如下:
Method listMethod(List<Integer>) has the sameerasure listMethod(List<E>) as another method in type Foo
此错误的意思是说listMethod(List<Integer>)方法在编译时擦除类型后的方法是 listMethod(List<E>),它与另外一个方法重复,通俗地说就是方法签名重复。这就是加3泛型擦除引起的问题:在编译后所有的泛型类型都会做相应的转化。转换规则如下
List<String>、List<Integer>、List<T> 擦除后的类型为 List。
List<String>□擦除后的类型为List[]。
List<? extends E>、List<? super E> 擦除后的类型为 List<E>。
List< T extends Serializable & Cloneable> 擦除后为 List< Serializable〉。
明白了这些擦除规则,再看如下代码:
public static void main(String[] args) {
List<String> list =new ArrayList<String>();
list.add("abc");
String str = list.get(0);
}
经过编译器的擦除处理后,上面的代码与下面的程序是一致的:
public static voidmain(String[] args) {
List list = new ArrayList() ;
list.add("abcn);
String str = (String)list.get(0);
}
Java编译后的字节码中已经没有泛型的任何信息了,也就是说一个泛型类和一个普通类 在经过编译后都指向了同一字节码,比如F〇〇<T>类,经过编译后将只有一份Foo.class类, 不管是Foo<String>还是Foo<Integer>引用的都是同一字节码。Java之所以如此处理,有两个原因:
□避免JVM的大换血。C++的泛型生命期延续到了运行期,而Java是在编译器擦除掉 的,我们想想,如果JVM也把泛型类型延续到运行期,那么JVM就需要进行大量的重构工作了。
□版本兼容。在编译期擦除可以更好地支持原生类型(Raw Type),在Java 1_5或1.6平 台上,即使声明一个List这样的原生类型也是可以正常编译通过的,只是会产生警告 信息而已。
明白了 Java的泛型是类型擦除的,我们就可以解释类似如下的问题了:
(1) 泛型的class对象是相同的
每个类都有一个class属性,泛型化不会改变class属性的返回值,例如:
public static void main(String[] args) {
List<String> ls=new ArrayList<String>();
List<Integer> li=new ArrayList<Integer>();
System.out.println(ls.getClass()==li.getClass());
}
以上代码将返回为true,原因很简单,
List<String>和List<Integer>擦除后的类型都是List,没有任何区别。
(2) 泛型数组初始化时不能声明泛型类型如下代码编译时通不过:
List<String>[] ls=new ArrayList<String>[];
原因很简单,可以声明一个带有泛型参数的数组,但是不能初始化该数组,因为执行了类型擦除操作,List<Object>[]与List<String>[]就是同一回事了,编译器拒绝如此
声明。
(3) instanceof不允许存在泛型参数
List<String> list = new ArrayList<String>();
System.out.println(list instanceof List<String>);