问题
When I was reading Thinking in Java (4th Edition) recently, I got a problem about method binding in Java. First let's look at two definitions from the book:
- Connecting a method call to a method body is called binding.
- All method binding in Java uses late binding unless the method is static or final.
you can find those definitions in section Method-call binding from chapter Polymorphism. (page 281-282)
To testify that, I wrote the following code:
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()");
}
}
The execution result is:
BindingTest_Sub ovrLd()
BindingTest_Base ovrLd()
BindingTest_Sub ovrRd()
Based on this result, I have following questions:
- According to the definition from the book, since all of my methods are inheritable, Java will use late binding (dynamic binding) for all three statements. However, I read about some other articles, which said that Java use static binding when dealing with overloading. It seems contradictory because obviously statement 1 is overloading.
- I do not fully understand why Java called ovrLd() of base class in statement 2. If it used dynamic binding, it should call overLd() of sub class because by the runtime JVM should be clear that sub2 is an instance of BindingTest_Sub class. On the other hand, if it used static binding, it should also call overLd() of sub class because compiler is able to observe that the type of given argument is an Integer. Can you tell me what job has been done by compiler or JVM when it deals with statement 2.
- The outcome of statement 3 makes sense to me. But still, I am curious about how the compiler recognize it (ovrRd()) as a overriding method. In another words, how does compiler know that there is another class which has a method that overrides this ovrRd().
Any thoughts about above questions or Java method binding mechanism is appreciated. Also please feel free to point out my mistakes.
回答1:
TL;DR; You're not really overloading ovrLd(Object)
, not in runtime. The compiler uses compile time type information to decide which is the best virtual method to call. sub1
and sub2
have different compile time types. And sub1
's type has a different best match for ovrLb(Integer)
.
Explaining. You're wondering:
- if
sub1.ovrLd(new Integer(1))
callsBindingTest_Sub.ovrLd(Integer)
- then why is
sub2.ovrLd(new Integer(2))
is callingBindingTest_Base.ovrLd(Object)
In this case it works like this:
The compiler uses the compile time type information of the variable to decide which method to call.
The compile time type of sub2
is BindingTest_Base
, in runtime you are assigning a BindingTest_Sub
to it, but that's not relevant to the compiler.
The only method from BindingTest_Base that matches the parameters of that call is: BindingTest_Base.ovrLd(Object)
So the compiler issues a virtual call to orvLd(Object)
Now, the runtime method for a virtual call is decided based on the full signature of the method being invoked (name + parameters). And there is no overload for ovrLd(Object)
in BindingTest_Sub
So the base class method is invoked.
With sub1
the compiler has more information. sub1
's compile time type is BintdingTest_Sub
and there is a method that matches ovrLd(Integer)
in that class.
Looking at the bytecode you can see this clearly:
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
回答2:
After doing some research, I think I understand what the author of Thinking in Java tried to convey.
The author said that All method binding in Java uses late binding unless the method is static or final.
I think this is true, but ambiguous. The ambiguity is from the term late binding. From my understanding, the binding here means the determination of specific implementation of method, not resolution of method (resolved to a symbol in symbol table). In other words, the compiler just refers a method to a symbol, but where in memory does that symbol points to is undetermined.
At the point of class loading, static method and final method (private method are implicitly final ) are associated with real memory address of the method (specific implementation). Because those method can not be overridden or even accessed by others, their implementation can not be changed dynamically. Except for those methods, other methods are bound to specific implementation at run time.
来源:https://stackoverflow.com/questions/29381292/method-binding-in-java