12.5.2 翻译泛型方法
类型擦除也会出现在泛型方法中。程序员通常认为下述的泛型方法
public static <T extends Comparable> T min(T[] a)
是一个完整的方法族,而擦除类型之后,只剩下一个方法
public static Comparable min(Comparable[] a)
注意,类型参数T已经被擦除了,只留下限定类型Comparable.
方法的擦除带来了两个复杂问题。看一看下面这个示例:
class DateInterval extends Pair<Date>{
public void setSecond(Date second){
if(second.compareTo(getFirst()) >= 0)
super.setSecond(second);
}
...
}
一个日期区间是一对Date对象,并且需要覆盖这人方法来确保第二个值永远不小于第一值。这个类擦除后变成
class DateInterval extends Pair{//after erasure
public void setSecond(Date second){....}
}
一个日期区间是一对Date对象,并且需要覆盖这个方法来确保第二值永远不小于第一个值。这个类擦除后变成
class DateInterval extends Pair{//after erasure
public void second(Date second){....}
}
令人感到奇怪的是,存在另一个从Pair继承的setSecond方法,即
public void setSecond(Object second)
这显然是一个不同的方法,因为它有一个不同类型的参数--Object,而不是Date。然而,不应该不一样。考虑下面的语句序列:
DateInterval interval = new DateInterval(...);
Pair<Date> pair = interval;
pair.setSecond(aDate);
这里,希望对setSecond的调用具有多态性,并调用最合适的那个方法,由于pair引用DateInterval对象,所以应该调用DateInterval.setSecond。问题在于类型擦除与多态发生了冲突。要解决这个问题,就需要编译器在DateInterval类中生成一个桥方法
(bridge method):
public void setSecond(Object second){setSecond((Date)second);
要想了解它的工作工程,请仔细地跟踪下列语句的执行:
pair.setSecond(aDate)
变量Pair已经声明为类型Pair<Date>,并且这个类型只有一个简单的方法叫setSecond,即setSecond(Object)。虚拟机用pair引用的对象调用这个方法。这个对象是DateInterval类型的,因而将会调用DateInterval.setSecond(Object)方法。这个方法是合成的桥方法。它调用DateInterval.setSecond(Date),这正是我们所期望的操作效果。
桥方法可能会变得十分奇怪。假设DateInterval方法也覆盖了getSecond方法:
class DateInterval extends Pair<Date>{
public Date getSecond(){
return (Date) super.getSecond().clone();
}
...
}
在擦除的类型中,有两个getSecond方法:
Date getSecond() //defined in DateInterval
Object getSecond() //overrides the method defined in Pair to call the first method
不能这样编写Java代码(在这里,具有相同参数类型的两个方法是不合法的)。它们都没有参数。但是,在虚拟机中,用参数类型和返回类型确定一个方法。因此,编译器可能产生两个仅返回类型不同的方法字节码,虚拟机能够正确处理这一情况