Java Generic List<List<? extends Number>>
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>>();
I'm not very familiar with Java syntax but it seems that your issue is this:
Covariance & Contravariance
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.
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.