Lists with wildcards cause Generic voodoo error

oligofren picture oligofren · Mar 23, 2011 · Viewed 18.7k times · Source

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);

Answer

Paŭlo Ebermann picture Paŭlo Ebermann · Mar 23, 2011

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