Java的泛型是类型擦除的

来源:互联网 发布:计算器软件下载 编辑:程序博客网 时间:2024/06/06 02:41

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>);

0 0
原创粉丝点击