2

This might sound a little bit trivial, but I'm really not able to find the crux of the matter.

List<Dog> dogs = getListOfDogs();
List<Cat> cats = getListOfCats();

feed(dogs);
feed(cats);

public void feed(List<Animal> animals) {
    // feed the animal:
    // depending on the type of animal, I might want to give it
    // different kind of food.
}

class Dog implements Animal { /**/ }
class Cat implements Animal { /**/ }
interface Animal { /**/ }

I'm in a context very similar to the one depicted above. Let's also suppose that getListOfDogs and getListOfCats are fixed, and there's no way to act on that side.

Of course, as put in that way, it's an illegal code: feed accepts only List<Animal> types, while I can only have List<Cat> and List<Dog>.

Since feed is a function that is almost identical for the two animals, with the exception of the kind of food (and I can manage it via instanceof), I would like to avoid copying it, just changing the signatures.

Is there any way to super-cast the two lists? Something like this (that is obviously incorrect): feed((List<Animal>) getListOfDogs());

Gian Segato
  • 2,359
  • 3
  • 29
  • 37
  • 4
    `feed` should accept `List extends Animal>`. See: https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html – Piotr Praszmo Jul 12 '15 at 09:40
  • 2
    In case you haven't seen it there is useful discussion around generics and collections here https://stackoverflow.com/questions/2723397/java-generics-what-is-pecs – beresfordt Jul 12 '15 at 09:45

3 Answers3

6

What you're looking for is:

public void feed(List<? extends Animal> animals) {

This will accept a List<Animal>, or a List<Dog>, or a List<Cat>, or so on. You won't be able to add anything to animals, since it could be any type of list. If you access an item in animals you'll get back a reference of type Animal.

user253751
  • 57,427
  • 7
  • 48
  • 90
4

You can do what you've said you want, by declaring feed like this:

public void feed(List<? extends Animal> animals)

However, you may well not want to do that, because of your subsequent statement:

Since feed is a function that is almost identical for the two animals, with the exception of the kind of food (and I can manage it via instanceof)...

Don't reach for instanceof lightly, it's usually the wrong tool. Instead, parameterize feed:

private void feed(List<? extends Animal> animals, Food food)

...and then

public void feed(List<Dog> animals) {
    this.feed(animals, this.dogFood);
}

and

public void feed(List<Cat> animals) {
    this.feed(animals, this.catFood);
}

...or similar.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

If it doesn't let you cast directly to List<Animal>, then you could cast to a List without the generic parameter and then to List<Animal>, but this is not a good approach.

This is not a good approach, because once you change the generic parameter you would be able to put cats into your list of dogs.

Instead, the feed function's parameter should look different

public void feed(List<? extends Animal> animals) {
    // feed the animal:
}

This way you will be able to get the animals out from the list as Animals, but won't be able to put animals of the wrong type into the list.

Nicholas Daley-Okoye
  • 2,267
  • 1
  • 18
  • 12