在 Java 中调用逆变方法
Invocation of contravariant methods in Java
假设情况如下:
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?
中实施
Java 从其父级继承所有方法,除非它们已被覆盖或隐藏。这意味着您的 B
class 与
相同
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 添加方法以提供向后兼容性,此编译代码不会改变其行为。
起初看起来您应该能够覆盖一个方法并接受更通用的参数(类似于您可以覆盖一个方法和 return 更 具体类型).
但是,这会给语言带来很大的复杂性。例如,
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。
假设情况如下:
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?
中实施Java 从其父级继承所有方法,除非它们已被覆盖或隐藏。这意味着您的 B
class 与
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 添加方法以提供向后兼容性,此编译代码不会改变其行为。
起初看起来您应该能够覆盖一个方法并接受更通用的参数(类似于您可以覆盖一个方法和 return 更 具体类型).
但是,这会给语言带来很大的复杂性。例如,
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。