Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specializing method arguments in subclasses in Java

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?

like image 518
Gabriele Avatar asked Nov 25 '16 15:11

Gabriele


People also ask

Which methods can be defined in a subclass?

This means that a subclass can define additional overloaded methods that add to the overloaded methods provided by a superclass. A subclass can do more than that; it can define a method that has exactly the same method signature (name and argument types) as a method in its superclass.

How do you call a method in subclass?

Subclass methods can call superclass methods if both methods have the same name. From the subclass, reference the method name and superclass name with the @ symbol.

Do subclasses automatically inherit methods?

them yourself. A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.


2 Answers

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) {}
}
like image 178
Andy Turner Avatar answered Nov 02 '22 23:11

Andy Turner


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.

like image 37
GhostCat Avatar answered Nov 03 '22 00:11

GhostCat