Безопасна ли здесь дисперсия? - PullRequest
2 голосов
/ 31 марта 2012
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 ]

5 голосов
/ 31 марта 2012

Ваш Lion может съесть Meat, но он также может съесть любую пищу (например, шпинат).

Если ваш Lion не может съесть какой-либо Food, то онне может рассматриваться как реализация Animal.

. Это важно понимать, когда вы решаете использовать подклассы и наследование классов как средство конструирования программ: вы не делаете свои подклассы более конкретными, чем вашиинтерфейс или суперклассы.

Чтобы подклассификация работала способами, которые решают проблемы (вместо того, чтобы создавать проблемы), вам необходимо соблюдать это правило: All subclasses must be functionally equivalent to the super-class (Liskov Substitution Principle) Это означает, что триклассы, которые обеспечивают доступ к базе данных к трем различным базам данных, являются хорошим кандидатом на роль подклассов общего класса (или, возможно, общего интерфейса), поскольку «функциональность» - это «предлагать доступ к базе данных».

Где ваш Lion пример не соответствует тому, что согласно вашему определению Animal реального мира, Львы не являются Animal с, потому что в реальном мире Львы не едятвид Food.Львы в реальном мире более специфичны в еде, чем в общем определении неизвестного животного.И именно это функциональное различие делает моделирование реальных львов подклассами этого конкретного определения Animal плохой подгонки.

Вы можете легко это исправить, если Animal "съесть пищу«метод выбросить IncompatibleFoodException, который меняет определение Animal с чего-то, что« ест пищу », на то, что« ест или отвергает пищу ».

2 голосов
/ 31 марта 2012

Это нарушает принцип замены Лискова , поэтому его, скорее всего, следует избегать (как говорит Эдвин, это действительно не животное, если оно не может есть пищу).

Возможно, против первогодумал, что это на самом деле не приведет к ошибке, а скорее вызовет 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"

...