Reputation: 335
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.
Upvotes: 3
Views: 149
Reputation: 79838
This is two questions for the price of one. (1) Why do we get the implementation from class B, and (2) why do we get the version of the method with the parameter of type A.
For question (1), the thing to remember is that the class of an object doesn't change when you cast it, or assign it to a variable whose type is something different. So in your example, the class of this
is always C
, because that's what you created. Class C
inherits its version of met(A a)
from class B
because it doesn't have its own version, and because class B
has overridden the version in class A
. This is what polymorphism is all about - the version of the method depends on the class of the object that you call it on, not on the type of the expression that you use to call it.
For question (2), it's a wee quirk of Java that method signatures are evaluated at compile time. So the compiler sees that you're passing an expression of type A
into your method, so it chooses the signature met(A a)
. Because this decision is made at compile time, the actual class of the argument doesn't make any difference - the compiler has already chosen the method, based on the type of the expression. In other words, Java doesn't give you polymorphism of method parameters.
Upvotes: 2
Reputation: 1500465
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.
Upvotes: 6