Why doesn't the diamond operator work within a addAll() call in Java 7?

Pradeep Kumar picture Pradeep Kumar · Sep 26, 2011 · Viewed 12.6k times · Source

Given this example from the generics tutorial.

List<String> list = new ArrayList<>();
list.add("A");

// The following statement should fail since addAll expects
// Collection<? extends String>

list.addAll(new ArrayList<>());

Why does the last line not compile, when it seems it should compile. The first line uses a very similar construct and compiles without a problem.

Please explain elaborately.

Answer

Joachim Sauer picture Joachim Sauer · Sep 26, 2011

First of all: unless you're using Java 7 all of this will not work, because the diamond <> has only been introduced in that Java version.

Also, this answer assumes that the reader understands the basics of generics. If you don't, then read the other parts of the tutorial and come back when you understand those.

The diamond is actually a shortcut for not having to repeat the generic type information when the compiler could find out the type on its own.

The most common use case is when a variable is defined in the same line it's initialized:

List<String> list = new ArrayList<>(); // is a shortcut for
List<String> list = new ArrayList<String>();

In this example the difference isn't major, but once you get to Map<String, ThreadLocal<Collection<Map<String,String>>>> it'll be a major enhancement (note: I don't encourage actually using such constructs!).

The problem is that the rules only go that far. In the example above it's pretty obvious what type should be used and both the compiler and the developer agree.

On this line:

list.addAll(new ArrayList<>());

it seems to be obvious. At least the developer knows that the type should be String.

However, looking at the definition of Collection.addAll() we see the parameter type to be Collection<? extends E>.

It means that addAll accepts any collection that contains objects of any unknown type that extends the type of our list. That's good because it means you can addAll a List<Integer> to a List<Number>, but it makes our type inference trickier.

In fact it makes the type-inference not work within the rules currently laid out by the JLS. In some situations it could be argued that the rules could be extended to work, but the current rules imply don't do it.