There are two valid methods that can be called for b.method(b);
:
final int method(A a) { return 4 + super.method(a); }
static int method(B b) { return 2 + b.method((A)b); } // inherited from A
The compiler chooses the more specific one - method(B)
. It doesn't matter that it is static. Static methods can be called on an instance too. See also: Is calling static methods via an object "bad form"? Why?
Now we need to resolve b.method((A)b);
. b
's compile time type is A
so the compiler only searches in A
. The argument expression's compile type is also A
. The only applicable method is:
int method(A a) { return 1; }
At runtime, b
is found to be an instance of B
, so the overridden method in B
is called instead:
final int method(A a) { return 4 + super.method(a); }
Now we resolve super.method(a);
. The types involved here are exactly the same as those in b.method((A)b);
, but we are using super
, so the implementation in the superclass of B
, i.e. in A
, is called instead:
int method(A a) { return 1; }
For b.method(c);
, the argument's compile time type is C
, so we have three applicable methods:
final int method(A a) { return 4 + super.method(a); }
static int method(B b) { return 2 + b.method((A)b); } // inherited from A
int method(C c) { return 8 + this.method((B)c); }
Again, the most specific, method(C)
is chosen.
Then we resolve this.method((B)c);
. This is resolved in exactly the same way as b.method(b);
. The compile-time types are the same. The fact that the runtime type of the parameter is a C
object doesn't really matter, because all C
overrides is the method(C)
method, which we never called when resolving b.method(b);
.