Generics:List<? Extends Animal> Is Same as List<Animal>

Generics : List? extends Animal is same as ListAnimal?

List<Dog> is a subtype of List<? extends Animal>, but not a subtype of List<Animal>.

Why is List<Dog> not a subtype of List<Animal>? Consider the following example:

void mySub(List<Animal> myList) {
myList.add(new Cat());
}

If you were allowed to pass a List<Dog> to this function, you would get a run-time error.


EDIT: Now, if we use List<? extends Animal> instead, the following will happen:

void mySub(List<? extends Animal> myList) {
myList.add(new Cat()); // compile error here
Animal a = myList.get(0); // works fine
}

You could pass a List<Dog> to this function, but the compiler realizes that adding something to the list could get you into trouble. If you use super instead of extends (allowing you to pass a List<LifeForm>), it's the other way around.

void mySub(List<? super Animal> myList) {
myList.add(new Cat()); // works fine
Animal a = myList.get(0); // compile error here, since the list entry could be a Plant
}

The theory behind this is Co- and Contravariance.

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.

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

Correct Generics using for List

It looks like you want

public static <T extends Animal> List<T> doSomeWork(List<T> animals){
List<T> newAnimals = new ArrayList<>();
return newAnimals;
}

Java Generics create list of objects which extends class A and implements interface B

You can also use '&' operator and declare the unknown class as a type parameter like below. The advantage of this vs declaring a class XXX extends B implement A is that your code will work with any such a class that satisfy the constraints not just XXX descendants.

import java.util.*;

interface A {};

class B {};

class Test {

public <T extends B & A> List<T> asList(Collection<T> elements) {
List<T> result = new ArrayList<>(elements.size());
for (T element : elements) result.add(element);
return result;
}
}

Why doesn't this list throw a ClassCastException?

Your dogs variable is of type List which is a raw type. It is not of type List<Dog>.

And since it is a raw type, you can add any object to it. Your Cat instance is not casted to anything.

Generics only provide compile-time safety and check, they are not used at runtime.

Read more about raw types in the Java Language Specification 4.8. Raw Types.



Related Topics



Leave a reply



Submit