2016-03-08 61 views
2

在java中,为什么covariant返回类型在非协变返回类型产生编译时错误时可以接受。当然,如果JVM可以处理协变返回类型,那么它可以处理非协变返回类型。我认为,当java看到一个带有协变返回值的重载方法时,它只会应用与调用对象关联的方法。为什么不能发生同样的非协变返回类型。 我的猜测是,它违背了超类方法合约的条款,当然如果允许这样做,那么子类(重载)方法的行为不是非常可预测的(因为在返回类型中没有一致性)?Java Co-variant返回

下面是一个例子(假设狗食是食品的一个子类,但CATFOOD不是食品的子类):

动物类

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

Cat类

public class Cat extends Animal { 

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

    return new CatFood(); 
} 
} 
+2

我没有真正明白你的观点。这可能是一个很好的问题,但请用一个例子来解释它。这对我们所有人都会更好。 –

回答

0

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

UPDATE: 的javac编译合作变的方法来它的基类的方法,当你拨打电话给它,基类方法委托调用子类方法。由于它们返回不同的类型,因此类型转换是不可能的。由于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; 
    } 
} 
+0

在我的例子所有三种方法具有相同的签名。但是,它们都有不同的回报类型。在协变的情况下,编译器不会抱怨,并且可以选择调用哪种方法;它根据对象类型确定调用哪个方法。在Cat的例子中,Catfood不是Food的协变,因此Cat的seekFood方法不能编译。这是为什么?我怀疑JVM可以把它当作协变,但是如果它确实如此,那么我猜你会有不可预知/紧密耦合的代码? – Risteard

0
class A { 
    Ra f(Pa x) { ... } 
} 

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

继承规则确定所述共变/双重变体的行为:

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

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

现在,如果编译器满足这实际上可以是一个B中的一个目的,那么它遵循B.f是一个有效的控制装置:

  1. 铅可以仅有效时,它帕或一个父类(双重变体);

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

  2. Rb只有在Ra或子类(co-variant)时才有效;

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

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

对于手头的情况,当DogFood是Food的孩子时,猫可能会返回DogFood。所以任何动物的食物确实是一种食物,即使动物真的是猫。