tags:

views:

3606

answers:

4

How come in java we cannot do:

List<List<? extends Number>> aList = new ArrayList<List<Number>>();

Even though this is OK:

List<? extends Number> aList = new ArrayList<Number>();

Compiler error message is:

Type mismatch: cannot convert from ArrayList<List<Number>> to List<List<? extends Number>>

+1  A: 

I'm not very familiar with Java syntax but it seems that your issue is this:

Covariance & Contravariance

eulerfx
+1  A: 

Your statement does not compile because List<? extends Number> is not the same type as List<Number>. The former is a supertype of the latter.

Have you tried this? Here I'm expressing that the List is covariant in its type argument, so it will accept any subtype of List<? extends Number> (which includes List<Number>).

List<? extends List<? extends Number>> aList = new ArrayList<List<Number>>();

Or even this. Here the type parameter for the ArrayList on the right-hand side is the same as the type parameter on the left-hand side, so variance is not an issue.

List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();

You should be able to just say

List<List<Number>> aList = new ArrayList<List<Number>>();

I tend to avoid the ? type wildcard whenever possible. I find that the expense incurred in type annotation is not worth the benefit.

Apocalisp
These two suggestions will work, but it still doesn't explain the fact that the original statement doesn't work.
+2  A: 

You should definitely use the ? type wildcard when appropriate, do not avoid it as a general rule. For example:

public void doThingWithList(List<List<? extends Number>> list);

allows you to pass a List<Integer> or a List<Long>.

public void doThingWithList(List<List<Number>> list);

allows you to only pass arguments declared as List<Number>. A small distinction, yes, but using the wildcard is powerful and safe. Contrary to how it may seem, a List<Integer> is not a subclass, or is not assignable, from List<Number>. Nor is List<Integer> a subclass of List<? extends Number, which is why the code above does not compile.

Steve Reed
+19  A: 

In Java, if Car is a derived class of Vehicle, then we can treat all Cars as Vehicles; a Car is a Vehicle. However, a List of Cars is not also a List of Vehicles. We say that List<Car> is not covariant with List<Vehicle>.

Java requires you to explicitly tell it when you would like to use covariance and contravariance with wildcards, represented by the ? token. Take a look at where your problem happens:

List<List<? extends Number>> l = new ArrayList<List<Number>>();
//        ----------------                          ------
// 
// "? extends Number" matched by "Number". Success!

The inner List<? extends Number> works because Number does indeed extend Number, so it matches "? extends Number". So far, so good. What's next?

List<List<? extends Number>> l = new ArrayList<List<Number>>();
//   ----------------------                    ------------
// 
// "List<? extends Number>" not matched by "List<Number>". These are
//   different types and covariance is not specified with a wildcard.
//   Failure.

However, the combined inner type parameter List<? extends Number> is not matched by List<Number>; the types must be exactly identical. Another wildcard will tell Java that this combined type should also be covariant:

List<? extends List<? extends Number>> l = new ArrayList<List<Number>>();
John Feminella
An exemplary explanation, indeed. +1
Adeel Ansari
Seems like List<List<? extends Number>> on the LHS would better match the poster's intent.
erickson