6

Consider the following situation:

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

public abstract class Animal{

public void eat(AnimalFeed somethingToEat)

}

Now I would like to define a class "Bird" extending "Animal" being sure that when the bird eats, it eats only BirdFeed.

One solution would be to specify a sort of contract, in which the caller of "eat" must pass an instance of the appropriate feed

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
    }
}
}

Is it acceptable to delegate the responsibility of the parameter to the caller? How to force the caller to pass a specialization of the parameter? Are there alternative design solutions?

2 Answers 2

13

You'd need to add a type variable to the class:

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

Then you can declare your subclasses as wanting a particular type of AnimalFeed:

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

public class Fish extends Animal<FishFeed> {
  public void eat(FishFeed somethingToEat) {}
}
Sign up to request clarification or add additional context in comments.

Comments

4

What you are asking for doesn't make sense from an theoretical point of view.

Restricting a method parameter violates the Liskov Substitution Principle.

The idea there: any occurance (usage) of some base class object must be able to deal with some sub class object, too.

A more simple example: when your base interface goes:

void foo(Number n)

then you must not do

@Override
void foo(Integer i)

in a subclass. Because all of a sudden, a caller

someObject.foo(someNumber)

would run into ugly ugly problems when someObject is of that derived class; which only accepts Integers, but not Numbers.

In other words: good OO design is much more than just writting down A extends B. You have to follow such rules; or you end up with systems are already broken on a conceptual point!

And for the record: it is theoretically valid to widen method parameters (in general, but in Java); and it is also ok to restrict the return types of methods (because these changes can not break client code; and that even works in Java).

Long story short: the answer here is too change your design; for example by using Generics and dependent interfaces to somehow create a relationship between the Animal and the Feed class hierarchy.

4 Comments

"it is very much valid to widen method parameters" In general, but not in Java: you can't widen parameter types because of the way that Java method resolution and overloading works. See stackoverflow.com/questions/18337056/… (although I'm sure there are better Qs and As than that).
I agree that wider parameters doesn't violate LSP. I'm just saying that wider arguments aren't supported in java (whereas narrower return types are; return type isn't part of the method signature used to determine which method to invoke).
Got it; updated my answer. Although I am wondering why your answers seems to fly six times better than mine ;-)
An example that the user could directly relate and use with no or little modifications make them fly better :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.