I have implemented the following code in order to understand the difference between static and dynamic binding:
class A {
int met(A a) {
return 0;
}
int met(B b) {
return 1;
}
int met(C c) {
return 2;
}
}
class B extends A {
int met(A a) {
return 3;
}
int met(B b) {
return 4;
}
int met(C c) {
return 5;
}
}
class C extends B {
int f() {
return ((A)this).met((A)this);
}
}
public class Test {
public static void main(String[] args) {
C x = new C();
System.out.println(x.f());
}
}
The result I get is 3, but I don't understand why, since the first cast is made to A.
So, let's look at the call:
((A)this).met((A)this);
That's equivalent to:
A target = this;
A argument = this;
target.met(argument);
So, for the last line, the compiler looks up the signature based on the compile-time types involved - it will look in A
(and superclasses) for a method called met
which has a parameter that is compatible with A
(the compile-time type of the argument). Overload resolution finds that the answer is:
int met(A a)
That's determined the signature at compile-time. However, the implementation of that method is determined at execution time, based on the execution-time target of the method call. That type is C
here - because this
is a reference to an instance of C
. (The f
method is called on an instance of C
.)
Now C
doesn't override int met(A a)
, but B
(its superclass) does - so that's the implementation that's used. It doesn't matter that B
also overrides met(B b)
and met(C c)
because the compiler has already determined that it's the met(A a)
method which is being called.
See more on this question at Stackoverflow