读Core java Volume I eleventh version Chapter 8 Generic Programming 8.5.3 Translating Generic Methods一节的时候,有一段论述初看并不是十分理解,原因是一时忘记了overload和override的区别,想来真是惭愧。特地记录下来,供大家一笑。
问题是这样的:
假设有一个泛型类,
public class Pair<T>{
private T first;
private T second;
public Pair() {
first = null;
second = null;
}
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
定义他的一个子类:
class DateInterval extends Pair<LocalDate>{
public void setFirst(LocalDate second) {
if (second.compareTo(getFirst()) >= 0)
super.setSecond(second);
} . . .
}
这里开发者的本意是override父类中的方法。但是在擦除之后,会遇到一个问题。具体说来,首先这个类被擦除为:
class DateInterval extends Pair // after erasure
{
public void setSecond(LocalDate second) { . . . }
. . .
}
此时,还存在着另外一个继承自Pair的setSecond方法,:
public void setSecond(Object second)
这是一个完全不同的方法,因为它有一个参数的类型是object而不是LocalDate。开发者的本意是利用java的多态机制以保证合适的方法在运行时被调用。但是很明显,这样做是有问题的。考虑以下代码:
var interval = new DateInterval(. . .);
Pair<LocalDate> pair = interval; // OK--assignment to superclass
pair.setSecond(aDate);
pair索引指向一个DateInterval的对象,当我们调用他的时候,是期望调用DateInterval.setSecond,然而,由于类型被擦除,多态受到了干扰,因此实际调用的是Pair.setSecond(Object)。为此,编译器生成了一个桥接(bridge)方法:
public void setSecond(Object second) { setSecond((LocalDate) second); }
我的理解:overload和override
个人认为这段交待的不是很清楚。文中只是强调了多态的一种形式override,而没有提及多态的另外一种形式overload。如果用overload来解释这个问题,我觉得会跟好理解一些。
开发者的本意是利用override机制实现多态,也就是说用
public void setSecond(LocalDate second)
override
public void setSecond(T newValue)
但是,由于编译阶段的擦除机制,override变成了overload,也就是说两个方法是并存的
public void setSecond(LocalDate second)
public void setSecond(Object newValue)
因此在编译阶段,以下代码并没有按照开发者的预期去绑定子类的方法到对象上。
对此,可以简单的做一个实验:
public class TestGenericMethod {
public static void main(String...strings){
Parent c = new Child();
c.setSecond(“”);
}
}
class Parent{
public void setSecond(Object second){
System.out.println("setSecond in parent");
}
}
class Child extends Parent{
public void setSecond(String second){
System.out.println("setSecond in Child");
}
}
运行代码,得到的输出为:
setSecond in parent
对此,该书的5.1.6节有详细的说明:
Next, the compiler determines the types of the arguments supplied in the method call. If among all the methods called f there is a unique method whose parameter types are a best match for the supplied arguments, that method is chosen to be called. This process is called overloading resolution …
“编译器决定了在方法调用时提供的变量类型。如果,在所有相同名字的方法中存在着一个唯一的方法,它的参数类型和提供的变量符合度最高,那么这个方法将被选择执行。这一过程称为overload…”
所以,这里的根本问题是由于type参数的擦除,预期的override变成了overload。
网友评论