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代码(在这里,具有相同参数类型的两个方法是不合法的)。它们都没有参数。但是,在虚拟机中,用参数类型和返回类型确定一个方法。因此,编译器可能产生两个仅返回类型不同的方法字节码,虚拟机能够正确处理这一情况

results matching ""

    No results matching ""