Consider the following code:
public class Main {
static class Animal {}
static class Dog extends Animal {}
static List<? extends Animal> foo() {
List<Dog> dogs = new ArrayList<>();
return dogs;
}
public static void main(String[] args) {
List<Animal> dogs = Main.foo(); // compile error
}
}
I'm trying to understand why it won't compile. Meaning, why doesn't the compiler let me refer to List<? extends Animal>
as a List<Animal>
?
Is that has something to do with the type erasure mechanism?
For example, if you have a List of Animals, it makes sense that a couple could be Goats, and some of them Cats, etc - right? Of course, the following would not be valid: With List<? extends Animal>, you're making a statement about the type of list you're dealing with. This is actually not a declaration of the type of object L can hold.
List<? extends Animal> means we can stuff any object in the List which IS A Animal Can someone help me know the difference between the above two? To me extends just sound redundant here. Thanks! Show activity on this post. List<Dog> is a subtype of List<? extends Animal>, but not a subtype of List<Animal>.
But now all the compiler knows about L is that it is a List of [either Animal or a subtype of Animal]s Because for all we know, L could be referencing a list of Goats - to which we cannot add an Animal. In the above, we're trying to cast an Animal as a Goat.
If you use super instead of extends (allowing you to pass a List<LifeForm> ), it's the other way around. The theory behind this is Co- and Contravariance. Show activity on this post. With List<Animal>, you know what you have is definitely a list of animals.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With