Java 协变 returns

Java Co-variant returns

在 java 中,为什么当非协变 return 类型产生编译时错误时,协变 return 类型是可接受的。当然,如果 JVM 可以处理协变 return 类型,那么它也可以处理非协变 return 类型。我假设当 java 看到一个具有协变 return 的重写方法时,它只应用与调用对象关联的方法。为什么非协变 return 类型不能发生同样的情况。 我的猜测是,这与违反 superclass' 方法合同的条款有关,当然,如果允许这样做,那么 sub class(覆盖)方法的行为不是很可预测(因为 return 类型不一致)?

举个例子(假设 DogFood 是 Food 的子class,但 CatFood 不是 Food 的子class):

动物class

public class Animal {

public Food seekFood() {

    return new Food();
}
}

狗class

public class Dog extends Animal {

public DogFood seekFood() { //This is OK since its a covariant

    return new DogFood();
}
}

猫class

    public class Cat extends Animal {

public CatFood seekFood() { // This won't compile. Catfood is not covariant

    return new CatFood();
}
}

如果两个方法具有相同的签名(方法名称和参数类型),编译器将无法决定选择调用哪个方法。如果两个方法具有相同的名称,但不同的参数类型和不同的 return 类型 - 它们具有不同的签名,然后编译器可以选择调用哪一个。

更新: javac 将协变方法编译为其基础 class 方法,当您调用它时,基础 class 方法委托调用 subclass 方法。由于它们 return 不同的类型,因此无法进行类型转换。感谢 A. Sundararajan's Weblog ,这个过程可以用一个代码片段非常清楚地解释:

class CircleFactory extends ShapeFactory {
    public Circle newShape() {
       // your code from the source file
       return new Circle();
    }

    // javac generated method in the .class file
    public Shape newShape() {
       // call the other newShape method here -- invokevirtual newShape:()LCircle;
    } 
}
class A {
    Ra f(Pa x) { ... }
}

class B extends A {
    @Override
    Rb f(Pb x) { ... }
}

继承规则决定co-variant/contra-variant行为:

类型 T 的值只能分配给类型 T 或 parent 类型的变量。

方法调用意味着将其实际方法参数 (1) 分配给局部参数 (2),并将结果值 (1) 分配给要使用的某个位置 (2)。

现在,如果编译器遇到实际上可以是 B 的 A object,那么 B.f 就是一个有效的覆盖:

  1. Pb可能只有Pa或者a时才有效 parent class (contra-variant);

    因为你B.f至少能接收到Pa值

  2. Rb 可能只有当它是 Ra 或 a child class (co-variant);

    时才有效

    因为 B.f 必须 return 可以分配给 Ra 的东西。

这使得 child class 更严格、更具体。

在手头的案例中,当 DogFood 是 child 食物时,Cat 可能会 return DogFood。所以任何动物的食物确实是食物,即使动物实际上是猫。