Java泛型小结(三)

Java泛型总结(三)

 

泛型代码和虚拟机
1)类型变量的擦除
  虚拟机并没有泛型类型的对象,所有的对象都是普通类,即使是我们看到的泛型类型(如Pair<String>),在编译后都成了普通的类型。任何时候,泛型类型都自动对应一个相应的原型(raw type),就是删除类型变量后的泛型类型名。
  如何擦除类型变量呢。对于有限定类型的,如T extends Comparable 的删除T,并替换成限定的类型Comparable ;没有限定类型的则替换成Object。
  注意到,如果有多个限定类型的呢,例如T extends Serializable & Comparable 
  这个时候,将把T替换成Serializable,因为编译器用的是限定类型中的第一个。但是,替换成Serializable,编译器在编译的时候当需要接口Comparable的compareTo方法时又需要进行强制转换,为了不影响效率,应该替换成Comparable比较好。那么,我们应该把Comparable放在前面,即:T extends Comparable  & Serializable。
  因此,我们总结出结论:为了提高效率,应该把tagging interfaces(标签接口,就是没有方法的接口)放在限定类型列表的最后。

2)泛型的翻译
  当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。
  When you program a call to a generic method, the compiler inserts casts when the return type has been erased
  接着第一篇中的例子:
        Pair<Employee> pair = new Pair<Employee>();
        Employee first = pair.getFirst();
 本来getFirst方法返回的类型是T,按照上面所述的类型擦除后返回类型变为Object,编译器又自动插入Employee的强制类型转换,所以直接就得到了Employee类型了。

3)桥方法(bridge method)
  桥方法用来保持多态,解决类型擦除与多态的冲突。
  例如,存在泛型类Pair<T>
public class Pair<T> {
     private second ;

     public void setSecond(T second) {
        this. second = second;
    }
}

自定义类MyDate继承了Pair<Date>
class MyDate extends Pair<Date> {
    @Override
    public void setSecond(Date second) {
        …… ;
    }
}

在进行类型擦除后变成了:
class MyDate extends Pair { // 去掉了泛型变量Date
    @Override
    public void setSecond(Date second) {
        …… ;
    }
}
当调用setSecond方法时:
        MyDate myDate = new MyDate();
        Pair<Date> pair = myDate;
        pair.setSecond( new Date());
这里的pair声明是Pair<Date>类型,并且仅有一个方法setSecond(Object second),但它实际引用的是MyDate对象,那么语句pair.setSecond( new Date());在调用时,实际调用的是MyDate中的setSecond(Object second)方法,但是我们看到MyDate中并没有定义这个一个方法,而只有setSecond(Date second)方法。这时,动态和擦除的冲突就出现了。那么,桥方法就出现了,编译器在编译的时候,为MyDate生成这样的一个方法:
public void setSecond(Object second){ setSecond(Date) second; }
也就是,对其中的入参second做了强制转换成Date类型了。


Java泛型小结(三)