2

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

这是一个示例(假设 DogFood 是 Food 的子类,但 CatFood 不是 Food 的子类):

动物类

public class Animal {

public Food seekFood() {

    return new Food();
}
}

狗类

public class Dog extends Animal {

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

    return new DogFood();
}
}

猫类

    public class Cat extends Animal {

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

    return new CatFood();
}
}
4

2 回答 2

0

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

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

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;
    } 
}
于 2016-03-08T18:51:56.127 回答
0
class A {
    Ra f(Pa x) { ... }
}

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

继承规则确定协变/反变行为:

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

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

现在,如果编译器遇到一个实际上可以是 B 的 A 对象,那么它遵循 forB.f是一个有效的覆盖:

  1. Pb 可能仅在它 Pa 或父类(逆变)时才有效;

    因为您B.f必须能够接收至少一个 Pa 值。

  2. Rb 可能只有当它是 Ra 或子类(协变)时才有效;

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

这使得子类更具限制性、更具体。

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

于 2016-03-08T20:27:48.267 回答