Lists with wildcards cause Generic voodoo error

Does anyone know why the following code does not compile? Neither add() nor addAll() works as expected. Removing the "? extends" part makes everything work, but then I would not be able to add subclasses of Foo.

 List<? extends Foo> list1 = new ArrayList<Foo>();
 List<? extends Foo> list2 = new ArrayList<Foo>();

 /* Won't compile */
 list2.add( new Foo() ); //error 1
 list1.addAll(list2);    //error 2 

error 1:

IntelliJ says:

add(capture<? extends Foo>) in List cannot be applied to add(Foo)

The compiler says:

cannot find symbol
symbol  : method addAll(java.util.List<capture#692 of ? extends Foo>)
location: interface java.util.List<capture#128 of ? extends Foo>

error 2:

IntelliJ gives me

addAll(java.util.Collection<? extends capture<? extends Foo>>) in List cannot be applied to addAll(java.util.List<capture<? extends Foo>>)

Whereas the compiler just says

cannot find symbol
symbol  : method addAll(java.util.List<capture#692 of ? extends Foo>)
location: interface java.util.List<capture#128 of ? extends Foo>
        list1.addAll(list2);

(I assume here that Bar and Baz are both subtypes of Foo.)

List<? extends Foo> means a list of elements of some type, which is a subtype of Foo, but we don't know which type. Examples of such lists would be a ArrayList<Foo>, a LinkedList<Bar> and a ArrayList<Baz>.

As we don't know which subtype is the type parameter, we can't put Foo objects into it, neither Bar or Baz objects. But we still know that the type parameter is a subtype of Foo, so every element already in the list (and which we can get from the list) must be a Foo object, so we can use Foo f = list.get(0); and similar things.

Such a list can only be used for taking elements out of the list, not to adding elements at all (apart from null, but I don't know if the compiler actually allows this).

A List<Foo> on the other hand allows adding any object which is a Foo object - and as Bar and Baz are subtypes of Foo, all Bar and Baz objects are Foo objects, so they can be added, too.


Remember PECS: Producer Extends, Consumer Super.

Since you are trying to add items to list2, it is a consumer and cannot be declared as List<? extends Foo>. But then you are using list2 as a producer also when you add it to list1. Therefore, list2 is both a producer and a consumer and must be a List<Foo>.

list1, as a pure consumer, can be a List<? super Foo>.


They're errors. Lets modify your code, considering that Bar and Baz are two different types extending Foo:

List<? extends Foo> list1 = new ArrayList<Bar>();
List<? extends Foo> list2 = new ArrayList<Baz>();

If list1.add(new Foo()) was allowed, you could add Foo instances in a collection containing Bar instances. This explains the first error.

If list1.addAll(list2) was allowed, all instances of Baz in list2 would be added to list1, which contains only Bar instances. This explains the second error.


Let me try to explain in what case you might need to use <? extend Classname>.

So, lets say you have 2 classes:

class Grand {
    private String name;

    public Grand(String name) {
        this.setName(name);
    }

    public Grand() {
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Dad extends Grand {
    public Dad(String name) {
        this.setName(name);
    }

    public Dad() {
    }
}

And lets say you have 2 collections, each contains some Grands and some Dads:

    List<Dad> dads = new ArrayList<>();
    dads.add(new Dad("Dad 1"));
    dads.add(new Dad("Dad 2"));
    dads.add(new Dad("Dad 3"));


    List<Dad> grands = new ArrayList<>();
    dads.add(new Dad("Grandpa 1"));
    dads.add(new Dad("Grandpa 2"));
    dads.add(new Dad("Grandpa 3"));

Now, lets asume that we want to have collection, which will contain Grand or Dad objects:

        List<Grand> resultList;
        resultList = dads; // Error - Incompatable types List<Grand> List<Dad>
        resultList = grands;//Works fine

How we can avoid this? Simply use wildcard:

List<? extends Grand> resultList;
resultList = dads; // Works fine
resultList = grands;//Works fine

Notice, that you cant add new items in such (resultList) collection. For more information you can read about Wildcard and PECS conseption in Java