2016-11-25 2 views
3

Рассмотрим следующую ситуацию:Специализируется аргументы методов в подклассах в Java

public abstract class AnimalFeed{ 
} 
public class FishFeed extends AnimalFeed{ 
} 
public class BirdFeed extends AnimalFeed{ 
} 

public abstract class Animal{ 

public void eat(AnimalFeed somethingToEat) 

} 

Теперь я хотел бы определить класс «Bird» простирающуюся «Animal» быть уверен, что, когда птица ест, он ест только BirdFeed.

Одно решение было бы указать вид договора, в котором вызывающий «съесть» должен пройти экземпляр соответствующего корма

public class Bird extends Animal{ 

@Override 
public void eat(AnimalFeed somethingToEat){ 

    BirdFeed somethingGoodForABird 

    if(somethingToEat.instanceOf(BirdFeed)){ 
    somethingGoodForABird = (BirdFeed) somethingGoodForABird 
    }else{ 
    //throws error, complaining the caller didn't feed the bird properly 
    } 
} 
} 

Приемлемо ли делегировать ответственность Параметром вызывающий? Как заставить вызывающего пройти специализацию параметра? Существуют ли альтернативные дизайнерские решения?

ответ

8

Вы должны были бы добавить переменную типа к классу:

public abstract class Animal<F extends AnimalFeed> { 
    public abstract void eat(F somethingToEat); 
} 

Тогда вы можете объявить свои подклассы, как желание конкретного типа AnimalFeed:

public class Bird extends Animal<BirdFeed> { 
    public void eat(BirdFeed somethingToEat) {} 
} 

public class Fish extends Animal<FishFeed> { 
    public void eat(FishFeed somethingToEat) {} 
} 
2

То, о чем вы просите, не имеет смысла с теоретической точки зрения .

Ограничение Параметр метода нарушает Liskov Substitution Principle.

Идеи есть: любая вхождение (использования) некоторый базового объекта класса должен быть в состоянии иметь дело с некоторыми субом объектом класса тоже.

Более простой пример: когда ваш интерфейс база идет:

void foo(Number n) 

тогда вы не должны сделать

@Override 
void foo(Integer i) 

в подклассе. Поскольку все внезапно, вызывающего

someObject.foo(someNumber) 

будет работать в некрасивые некрасивые проблемы, когда SomeObject является тем производным классом; который принимает только целые числа, но не числа.

Иными словами: хороший дизайн OO - это нечто большее, чем просто написание вниз A extends B. Вы должны следовать таким правилам; или вы в конечном итоге с системами уже сломан на концептуальных пункт!

И для записи: это теоретически действует до расширения параметров метода (в целом, но в Java); и это также нормально: ограничивает типы возвращаемых методов (поскольку эти изменения могут не разбить клиентский код, и это даже работает на Java).

Короткий рассказ: ответ здесь слишком изменяет ваш дизайн; например, используя Generics и зависимые интерфейсы, чтобы каким-то образом создать связь между иерархией классов Animal и Feed.

+0

«это очень действует для расширения параметров метода «В общем, но не в Java: вы не можете расширять типы параметров из-за того, как работает разрешение и перегрузка Java-метода. См. Http://stackoverflow.com/questions/18337056/wider-argument-types-of-overridding-method-in-java (хотя я уверен, что есть лучшие Qs и как это). –

+0

Я согласен, что более широкие параметры не нарушают LSP. Я просто говорю, что более широкие аргументы не поддерживаются в java (тогда как более узкие типы возвращаемых типов: тип возврата не является частью сигнатуры метода, используемой для определения того, какой метод вызывается). –

+1

Есть, обновил мой ответ. Хотя мне интересно, почему ваши ответы, кажется, летают в шесть раз лучше моих ;-) – GhostCat