Reputation: 1183
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?
Upvotes: 3
Views: 72
Reputation: 37655
At first it may appear that you should be able to override a method and accept more general arguments (analogous to the fact that you can override a method and return a more specific type).
However, it would introduce great complexity into the language. For example,
class A {
void foo(String s) {}
}
class B extends A {
void foo(CharSequence s) { System.out.println(true); }
void foo(Serializable s) { System.out.println(false); }
}
With your proposal, what is this supposed to print?
A a = new B();
a.foo("bar");
So the rule is simply that in order to override a method, you have to have the same argument list. (There are some situations when you can override when the argument lists are not identical, but have the same erasure). The precise definitions are given here.
Upvotes: 0
Reputation: 533670
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.
Upvotes: 1