Cast on object of type superclass returns object of type subclass in java
I have applied the following code 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 in A.
source to share
These are two questions for the price of one. (1) Why we get the implementation from class B and (2) why we get the version of the method with a parameter of type A.
In question (1), keep in mind that the class of an object does not change when it is created or assigns it to a variable of a different type. Therefore, in your example, the class is this
always C
, because this is what you created. The class C
inherits its version met(A a)
from the class B
because it does not have its own version, and because the class has B
overridden the version in the class A
. This is what polymorphism is all about - the version of a method depends on the class of the object you are calling it, not the type of expression you use to call it.
In question (2), it is a Java quirk that method signatures are evaluated at compile time. Therefore, the compiler sees that you are passing a type expression A
to your method, so it chooses the signature met(A a)
. Since this decision is made at compile time, the actual class of the argument is irrelevant - the compiler has already chosen a method based on the type of the expression. In other words, Java doesn't give you method parameter polymorphism.
source to share
So let's look at the call:
((A)this).met((A)this);
This is equivalent to:
A target = this;
A argument = this;
target.met(argument);
So, for the last line, the compiler looks for a signature based on the compile-time types involved - it will look in A
(and superclasses) for a named method met
that has a parameter compatible with A
(compile-time type argument). Overload resolution detects that the answer is:
int met(A a)
This determined the signature at compile time. However, the implementation of this method is determined at runtime based on the target runtime of the method call. This type is C
here because it this
is an instance reference C
. (The method f
is called on the instance C
.)
Now C
does not override int met(A a)
, but B
(its superclass) does - so the implementation used is. It doesn't matter which B
also overrides met(B b)
and met(C c)
, because the compiler has already determined that it is calling the method met(A a)
.
source to share