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
Why Are Getter and Setter Method Important in Java
Apache Httpclient Interim Error: Nohttpresponseexception
How to Compile a .Java with Support for Older Versions of Java
Jersey Maven Quickstart Archetype in Eclipse
Having a 3Rd Party Jar Included in Maven Shaded Jar Without Adding It to Local Repository
Java 8 Lambdas Group List into Map
How to Draw in JPAnel? (Swing/Graphics Java)
Java Swing; Two Classes, Where to Put If Statements and New Actionlisteners
Java: Join Array of Primitives with Separator
Jdbc Connection to Mssql Server in Windows Authentication Mode
How to Make Anonymous Inner Classes in Java Static
What's the Point of Guava's Optional Class
Lambdaconversionexception with Generics: Jvm Bug
How to Define Multiple Jbutton Actions from a Different Class
Java.Lang.Outofmemoryerror: Java Heap Space in Maven
How to Get All Table Names from a Database
How to Express Dependency in Maven on Java Ee Features for Transition to Java 9