这里的协方差是否安全?

时间:2012-03-30 21:13:40

标签: java oop inheritance covariance

class Food{}

class Meat extends Food{}

class Animal{
    void feed(Food f){}
}

class Lion extends Animal{
    void feed(Meat m){}
}


void foo(Animal a){
   Food f = new Food();
   a.feed(f);
}

如果我们发送给foo(new Lion())会怎样? 我知道它会出错,但我需要解释

2 个答案:

答案 0 :(得分:5)

你的Lion可以吃Meat,但它也可以吃任何食物(如菠菜)。

如果您的Lion无法使用任何类型的Food,则不能将其视为Animal的实施。

在决定使用子类和类继承作为构造程序的方法时,这一点至关重要:您不要使您的子类比您的接口或超类更具体。

对于以解决问题(而不是创建问题)的方式工作的子类,您需要遵守本指南:All subclasses must be functionally equivalent to the super-class (Liskov Substitution Principle)这意味着提供对三个不同数据库的数据库访问的三个类是好的从公共类(或可能共享一个公共接口)成为子类的候选者,因为“功能”是“提供数据库访问”。

根据您对Lion现实世界的定义,您的Animal示例不足之处,狮子会不是Animal,因为现实世界的狮子会不吃任何类型的Food。真实世界狮子的食物摄入能力比未知动物的一般定义更具特异性。正是这种功能差异使得将现实世界的狮子会建模为这个动物的特定定义的子类是不合适的。

您可以通过Animal“吃食物”方法轻松解决此问题,将IncompatibleFoodException从“吃食物”的内容更改为“{1}}” “吃或拒绝食物”。

答案 1 :(得分:2)

这违反了Liskov substitution原则,因此应该避免(正如Edwin所说,如果它不能吃食物,它真的不是动物)。

也许反对第一个想法,这实际上不会导致错误,而是按预期调用Animal :: feed而不是Lion :: feed。

重新阅读标题,并专门回答这个问题:不,协方差在这里是不安全的(就行为而言。就语法而言。)。

将问题快速复制并粘贴到示例中:

class Food{}

class Meat extends Food{}

class Animal{
    void feed(Food f){
        System.out.println("om nom nom");
    }
}

class Lion extends Animal{
    void feed(Meat m)
    {
        System.out.println("OM NOM NOM");
    }
}

public class test
{

    public static void main(String[] args)
    {
        foo(new Lion());
    }

    static void foo(Animal a){
       Food f = new Food();
       a.feed(f);
    }

}

输出“om nom nom”