A raw list converts to List> just fine. Why can\'t a list of raw lists convert to a list of List>?
{ // works
// #1 (does compile)
List raw = null;
List> wild = raw;
// #2 (doesn't compile)
List raw = null;
List> wild = raw;
First let's sort out why these are actually unrelated assignments. That is, they're governed by different rules.
#1 is called an unchecked conversion:
There is an unchecked conversion from the raw class or interface type (§4.8)
Gto any parameterized type of the formG.1,...,Tn>
Specifically it is a special case of an assignment context just for this scenario:
If, after [other possible conversions] have been applied, the resulting type is a raw type, an unchecked conversion may then be applied.
#2 requires a reference type conversion; however the problem with it is that it is not a widening conversion (which is the kind of reference conversion that would be implicitly allowed without a cast).
Why is that? Well, this is specifically governed by the rules of generic subtyping and more specifically this bullet point:
Given a generic type declaration
C(n > 0), the direct supertypes of the parameterized type1,...,Fn> C, where1,...,Tn> Ti(1 ≤ i ≤ n) is a type, are all of the following:
C, where1,...,Sn>SicontainsTi(1 ≤ i ≤ n).
This refers us to something the JLS calls containment, where to be a valid assignment, the arguments of the left-hand side must contain the arguments of the right-hand side. Containment largely governs generic subtyping since "concrete" generic types are invariant.
You may be familiar with the ideas that:
List is not a ListList is a List extends Animal>.Well, the latter is true because ? extends Animal contains Dog.
So the question becomes "does a type argument List> contain a raw type argument List"? And the answer is no: although List> is a subtype of List, this relationship does not hold for type arguments.
There is no special rule that makes it true: List is not a subtype of >
List for essentially the same reason List is not a subtype of List.
So because List is not a subtype of List, the assignment is invalid. Similarly, you cannot perform a direct narrowing conversion cast because >
List is not a supertype of List either.>
To make the assignment you can still apply a cast. There are three ways to do it that seem reasonable to me.
// 1. raw type
@SuppressWarnings("unchecked")
List> list0 = (List) api();
// 2. slightly safer
@SuppressWarnings({"unchecked", "rawtypes"})
List> list1 = (List>) (List extends List>) api();
// 3. avoids a raw type warning
@SuppressWarnings("unchecked")
List> list2 = (List>) (List super List>>) api();
(You can substitute JAXBElement for the inner List.)
Your use-case for this casting should be safe because List is a more restrictive type than >
List.
The raw type statement is a widening cast then unchecked assignment. This works because, as shown above, any parameterized type can be converted to its raw type and vice-versa.
The slightly safer statement (named as such because it loses less type information) is a widening cast then narrowing cast. This works by casting to a common supertype:
List extends List>
╱ ╲
List> List
The bounded wildcard allows the type arguments to be considered for subtyping via containment.
The fact that List extends List> is considered a supertype of List can be proven with transitivity:>
? extends List contains ? extends List>, because List is a supertype of List>.
? extends List> contains List>.
Therefore ? extends List contains List>.
(That is, List extends List> :> List extends List>> :> List.)>
The third example works in a way that's similar to the second example, by casting to a common supertype List super List>>. Since it doesn't use a raw type, we can suppress one less warning.
The non-technical summary here is that the specification implies that there is neither subtype nor supertype relationship between List and List.>
Although converting from List to List should be safe, it is not allowed. (It's safe because both are a >
List that can store any kind of List, but a List imposes more restrictions on how its elements can be used after they are retrieved.)>
There is unfortunately no practical reason this fails to compile except that raw types are strange and usage of them is problematic.