Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't List<? extends Animal> be replaced with List<Animal>?

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?

like image 666
yaseco Avatar asked Aug 04 '20 08:08

yaseco


People also ask

Can List<extends Animal> be used as a declaration of type?

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.

What is the difference between list<dog> and List<extends Animal>?

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

Why can't we add an animal to L?

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.

How to pass list<lifeform> to list<animal> instead of extends?

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.


1 Answers

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.
like image 137
Andy Turner Avatar answered Sep 28 '22 14:09

Andy Turner