【问题标题】:Is co-variance safe here?协方差在这里安全吗?
【发布时间】:2012-03-30 21:13:39
【问题描述】:
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()) 会发生什么? 我知道它会出错,但我需要解释一下

【问题讨论】:

  • 为什么你认为你会在这里得到一个错误? Lion 的 feed 方法不会覆盖 Animal 的 feed 方法,因此 animal 的 feed 方法会在 foo 中调用。如果您尝试在返回类型中使用子类,而不是在参数列表中使用,则会发生覆盖
  • 其实不会报错。 Lion 会吃掉食物,但会调用 Animal 中的方法,而不是 Lion 中更具体但不匹配的方法

标签: java oop inheritance covariance


【解决方案1】:

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

如果你的Lion 不能吃任何Food,那么它就不能被认为是Animal 的实现。

在决定使用子类化和类继承作为构建程序的一种方式时,理解这一点至关重要:不要让子类比接口或超类更具体。

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

您的Lion 示例不足之处在于,根据您对Animal 现实世界的定义,狮子不是Animals,因为现实世界的狮子不吃任何类型的Food。与未知动物的一般定义相比,现实世界的狮子在进食能力方面更为具体。正是这种功能差异使得将现实世界的狮子建模为动物的特定定义的子类并不合适。

您可以通过让Animal“吃食物”方法抛出一个IncompatibleFoodException 来轻松解决此问题,这会将Animal 的定义从“吃食物”更改为“吃或拒绝食物” .

【讨论】:

  • 这就是@Override 注释派上用场的地方,因为它表明Lion.feed(Meat) 不会覆盖任何东西。
【解决方案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”

【讨论】:

    猜你喜欢
    • 2023-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 1970-01-01
    • 2014-06-21
    相关资源
    最近更新 更多