最近在阅读Thinking in Java(第 4 版)时,我遇到了一个关于 Java 中方法绑定(bind)的问题。首先让我们看一下书中的两个定义:
- 将方法调用连接到方法体称为绑定(bind)。
- Java 中的所有方法绑定(bind)都使用后期绑定(bind),除非方法是静态的或最终的。
您可以在多态性一章的方法调用绑定(bind)部分找到这些定义。 (第 281-282 页)
为了证明这一点,我写了下面的代码:
public class Test3{
public static void main(String[] args) {
BindingTest_Sub sub1 = new BindingTest_Sub();
BindingTest_Base sub2 = new BindingTest_Sub();
sub1.ovrLd(new Integer(1)); // statement 1
sub2.ovrLd(new Integer(2)); // statement 2
sub2.ovrRd(); // statement 3
}
}
class BindingTest_Base {
void ovrLd(Object obj){
System.out.println("BindingTest_Base ovrLd()");
}
void ovrRd(){
System.out.println("BindingTest_Base ovrRd()");
}
}
class BindingTest_Sub extends BindingTest_Base{
void ovrLd(Integer i){
System.out.println("BindingTest_Sub ovrLd()");
}
void ovrRd(){
System.out.println("BindingTest_Sub ovrRd()");
}
}
执行结果为:
BindingTest_Sub ovrLd()
BindingTest_Base ovrLd()
BindingTest_Sub ovrRd()
根据这个结果,我有以下问题:
- 根据书上的定义,由于我的所有方法都是可继承的,Java 将对所有三个语句使用后期绑定(bind)(动态绑定(bind))。但是,我看了一些其他文章,其中说Java在处理重载时使用静态绑定(bind)。这似乎是矛盾的,因为显然陈述 1 是重载的。
- 我不完全理解为什么Java在语句2中调用基类的ovrLd()。如果它使用动态绑定(bind),它应该调用子类的overLd(),因为运行时JVM应该清楚sub2是一个实例BindingTest_Sub 类。另一方面,如果它使用静态绑定(bind),它还应该调用子类的 overLd(),因为编译器能够观察到给定参数的类型是 Integer。你能告诉我编译器或 JVM 在处理语句 2 时做了什么工作吗?
- 陈述 3 的结果对我来说很有意义。但是,我仍然很好奇编译器如何将它 (ovrRd()) 识别为覆盖方法。换句话说,编译器如何知道有另一个类有一个覆盖这个 ovrRd() 的方法。
如果您对上述问题或 Java 方法绑定(bind)机制有任何想法,我们将不胜感激。也请随时指出我的错误。
最佳答案
TL;DR; 您并没有真正重载 ovrLd(Object)
,不是在运行时。编译器使用编译时类型信息来决定调用哪个虚方法是最好的。 sub1
和 sub2
有不同的编译时类型。 sub1
的类型与 ovrLb(Integer)
有不同的最佳匹配。
解释。你想知道:
- 如果
sub1.ovrLd(new Integer(1))
调用BindingTest_Sub.ovrLd(Integer)
- 那为什么
sub2.ovrLd(new Integer(2))
调用BindingTest_Base.ovrLd(Object)
在这种情况下它是这样工作的:
编译器使用变量的编译时 类型信息来决定调用哪个方法。
sub2
的编译时类型是 BindingTest_Base
,在运行时你正在为其分配一个 BindingTest_Sub
,但这与编译器无关。
BindingTest_Base 中与该调用的参数匹配的唯一方法是:BindingTest_Base.ovrLd(Object)
因此编译器发出对 orvLd(Object)
的虚拟调用
现在,虚拟调用的运行时方法是根据被调用方法的完整签名(名称+参数)决定的。 BindingTest_Sub
中的 ovrLd(Object)
没有重载
所以基类方法被调用。
sub1
编译器有更多信息。 sub1
的编译时类型是BintdingTest_Sub
并且在该类中有一个匹配ovrLd(Integer)
的方法。
查看字节码你可以清楚地看到:
aload 1 // sub1
// ... blah blah blah creating the integer
// the last opcode issued by the compiler for "statement 1"
INVOKEVIRTUAL com/ea/orbit/actors/samples/helloworld/BindingTest_Sub.ovrLd (Ljava/lang/Integer;)V
aload 2 // sub2
// ... blah blah blah creating the integer
// the last opcode issued by the compiler for "statement 2"
INVOKEVIRTUAL com/ea/orbit/actors/samples/helloworld/BindingTest_Base.ovrLd (Ljava/lang/Object;)V
关于java - Java 中的方法绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29381292/