enum Note {
MIDDLE_C, C_SHARP, B_FLAT
}
class Instrument {
public void play(Note n) {
System.out.println("Instrument.play() " + n);
}
}
class Wind extends Instrument {
@Override
public void play(Note n) {
System.out.println("Wind.play() " + n);
}
}
class Brass extends Instrument {
@Override
public void play(Note n) {
System.out.println("Brass.play() " + n);
}
}
public class Music {
public static void tune(Instrument i) {
i.play(Note.B_FLAT);
}
public static void main(String[] args) {
Wind flute = new Wind();
tune(flute);
}
}
之后,在书中提到:
tune() 方法,它接受一个 Instrument 引用。那么在这种情况下,编译器怎样才能知道这个 Instrument 引用指向的是 Wind 对象,而不是 Brass 对象呢?实际上,编译器无法得知。
上述程序之所以令人迷惑,主要是因为前期绑定。因为,当编译器只有一个 Instrument 引用时,它无法知道究竟调用哪个方法才对。(这句话要怎么理解?)
我的疑问是:既然 tune() 内接的是 Wind 对象 flute,那么它就应该知道调用 Wind.play() 方法,而不是调用 Instrument.play() 方法或 Brass.play() 方法。如果 Wind 没有覆盖 play() 方法,那么最终应该调用基类 Instrument.play() 方法。于是书中所说的令人迷惑的地方在哪里?还有,引用中的那句话要怎么理解?
试想一下,如果该class被其它代码import,那么tune方法接受的参数的类型还一定是Wind吗?
所以,编译器其实不能确定tune方法接受的参数为Wind类型,只能确定它是代码中写的Instrument类型。JVM提供了invokevirtual指令,用于实现这个polymorphic调用。如果编译器换成invokespecial Wind.play,那这段代码的语义就不一样了。
你可能会想,如果将tune方法标记成private,是不是编译器就能够分析出tune方法始终只接受一个Wind类型的参数,期望它能将其优化成invokespecial,这样效率更高呢?其实程序员不用关心这个的,JVM会对invokevirtual自动作优化,不会每次调用都去查找是调用子类还是父类方法的。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。