Any Simple Way to Explain Why I Cannot Do List<Animal> Animals = New Arraylist<Dog>()

Any simple way to explain why I cannot do ListAnimal animals = new ArrayListDog()?

Imagine you create a list of Dogs. You then declare this as List<Animal> and hand it to a colleague. He, not unreasonably, believes he can put a Cat in it.

He then gives it back to you, and you now have a list of Dogs, with a Cat in the middle of it. Chaos ensues.

It's important to note that this restriction is there due to the mutability of the list. In Scala (for example), you can declare that a list of Dogs is a list of Animals. That's because Scala lists are (by default) immutable, and so adding a Cat to a list of Dogs would give you a new list of Animals.

Is ListDog a subclass of ListAnimal? Why are Java generics not implicitly polymorphic?

No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

Suddenly you have a very confused cat.

Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.

Why can't List? extends Animal be replaced with ListAnimal?

A List<Animal> is a List to which you can add any Animal (or null), and everything you take out of it will be an Animal.

A List<? extends Animal> is a list which contains only a specific subclass of Animal (or null), and you don't know which one; this allows you to treat everything you take out of it as an Animal, but you aren't allowed to add anything to it (except for literal null).


A List<? extends Animal> can't act as a List<Animal>, because that would allow you to do this:

List<Cat> listOfCats = new ArrayList<>();
List<? extends Animal> listOfSomeAnimals = listOfCats; // Fine.
List<Animal> listOfAnimals = listOfSomeAnimals; // Error, pretend it works.
listOfAnimals.add(new Dog());

Now, because listOfCats, listOfSomeAnimals and listOfAnimals are all the same list, the Dog has been added to listOfCats. As such:

Cat cat = listOfCats.get(0);  // ClassCastException.

Casting ListAnimal to ListDog

Generics inheritance is little different than java inheritance principle. You need to use ?(wildcards)

List<? extends Animal> dogList = getAnimalList();

EDIT:

Wildcard Guidelines:

  1. An "in" variable is defined with an upper bounded wildcard, using the extends keyword.
  2. An "out" variable is defined with a lower bounded wildcard, using the super keyword.
  3. In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
  4. In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.

Is it possible to get a list of Dog from a list of Animal without a cast?

Ok, it seems absurd but the problem was in the JSF code:

<ui:repeat var="animal" value="#{bean.animals.get('dogs')}" varStatus="status">

I don't know why, but changing var="animal" to anything else works. I do not need any cast.



Related Topics



Leave a reply



Submit