在Java中调用逆变方法

时间:2016-02-13 21:49:54

标签: java oop contravariance

鉴于以下情况:

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 
    }
}

此代码中的方法调用将按如下方式调度:

(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

我一直想知道的是,为什么Java将逆变方法(B.implementation)基本上视为一个重载(除了A.implementation和B.implementation的签名不是覆盖等价的)。有没有一个故意的理由来调度最具体的方法签名(如果是这样,你会指出我在Java规范中明确指出的那些地方吗?),或者它只是在Java中如何实现重写的偶然结果?

2 个答案:

答案 0 :(得分:1)

Java会从其父级继承所有方法,除非它们已被覆盖或隐藏。这意味着您的B类与

相同
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);
    }
}

这样做的好处就是说你有

class A {
    void implementation(Derived stuff) { 
    // Implementation A 
    }
}

class B extends A {
}

new B().implementation(new Derived());

如果稍后将方法添加到B提供向后兼容性,则此编译代码不会更改它的行为。

答案 1 :(得分:0)

首先,您可能看起来应该能够覆盖一个方法并接受更多一般参数(类似于您可以覆盖一个方法并返回更多特定类型的事实)。

然而,它会给语言带来很大的复杂性。例如,

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); }
}

根据你的提议,这应该打印什么?

A a = new B();
a.foo("bar");

所以规则就是为了覆盖一个方法,你必须拥有相同的参数列表。 (在某些情况下,当参数列表不相同但具有相同的擦除时,您可以覆盖)。给出了精确的定义here