Given a situation like the below:
interface Base { }
interface Derived extends Base{ }
interface OtherDerived extends Base{ }
class A {
void implementation(Derived stuff) {
// Implementation A
}
}
class B extends A {
// contravariant; does not override
void implementation(Base stuff) {
// Implementation B
}
}
Method calls in this code are dispatched like so:
(new B()).implementation(derivedObject); // Ex. 1: calls A.implementation
(new B()).implementation(baseObject); // Ex. 1: calls B.implementation
(new B()).implementation(otherDerivedObject()); // Ex. 2: calls B.implementation
What I've always wondered is, why does Java treat the contravariant method (B.implementation) essentially like an overload (other than that the signatures of A.implementation and B.implementation are not override equivalent). Is there an intentional reason for dispatching to the most specific method signature (and if so, would you point me to where in the Java spec this is explicitly spelled out?), or is it just an accidental consequence of how overriding is implemented in Java?
Java inherits all methods from it's parent unless they have been overridden or hidden. This means your B
class is the same as
class A {
void implementation(Derived stuff) {
// Implementation A
}
}
class B extends A {
// contravariant; does not override
void implementation(Base stuff) {
// Implementation B
}
void implementation(Derived stuff) {
super.implementation(stuff);
}
}
The advantage of this is that say you have
class A {
void implementation(Derived stuff) {
// Implementation A
}
}
class B extends A {
}
and
new B().implementation(new Derived());
this compiled code doesn't change it's behaviour if you later add a method to B provided backward compatibility.