Val Akkapeddi
Val Akkapeddi

Reputation: 1183

Invocation of contravariant methods in Java

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

Answers (2)

Paul Boddington
Paul Boddington

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

Peter Lawrey
Peter Lawrey

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

Related Questions