开发者社区> 问答> 正文

关于Java动态绑定的疑惑

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() 方法。于是书中所说的令人迷惑的地方在哪里?还有,引用中的那句话要怎么理解?

展开
收起
蛮大人123 2016-03-04 17:04:10 2275 0
1 条回答
写回答
取消 提交回答
  • 我说我不帅他们就打我,还说我虚伪

    试想一下,如果该class被其它代码import,那么tune方法接受的参数的类型还一定是Wind吗?
    所以,编译器其实不能确定tune方法接受的参数为Wind类型,只能确定它是代码中写的Instrument类型。JVM提供了invokevirtual指令,用于实现这个polymorphic调用。如果编译器换成invokespecial Wind.play,那这段代码的语义就不一样了。

    你可能会想,如果将tune方法标记成private,是不是编译器就能够分析出tune方法始终只接受一个Wind类型的参数,期望它能将其优化成invokespecial,这样效率更高呢?其实程序员不用关心这个的,JVM会对invokevirtual自动作优化,不会每次调用都去查找是调用子类还是父类方法的。

    2019-07-17 18:52:34
    赞同 展开评论 打赏
问答分类:
问答标签:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Spring Cloud Alibaba - 重新定义 Java Cloud-Native 立即下载
The Reactive Cloud Native Arch 立即下载
JAVA开发手册1.5.0 立即下载