Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 630
Pradeep Kumar Avatar asked Sep 26 '11 13:09

Pradeep Kumar


People also ask

What is the diamond operator in Java?

Diamond Operator: Diamond operator was introduced in Java 7 as a new feature. The main purpose of the diamond operator is to simplify the use of generics when creating an object. It avoids unchecked warnings in a program and makes the program more readable.

Which of the following options is the correct name for empty type parameter rectangle square diamond circle?

It's called diamond operator and infers the generic type from the left hand side, if possible.


2 Answers

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.

like image 116
Joachim Sauer Avatar answered Oct 12 '22 23:10

Joachim Sauer


The explanation from the Type Inference documentation seems to answer this question directly ( unless I'm missing something else ).

Java SE 7 and later support limited type inference for generic instance creation; you can only use type inference if the parameterized type of the constructor is obvious from the context. For example, the following example does not compile:

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

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

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

Note that the diamond often works in method calls; however, for greater clarity, it is suggested that you use the diamond primarily to initialize a variable where it is declared.

In comparison, the following example compiles:

// The following statements compile:

List<? extends String> list2 = new ArrayList<>();
list.addAll(list2);
like image 30
Kal Avatar answered Oct 12 '22 22:10

Kal