Let's look at a few similar examples:
public final class Test {
public Test() {
final ArrayList<ArrayList<Test>> a1 = new ArrayList<>();
final ArrayList<? extends Iterable<Test>> valueTypeRef = a1;
final Iterable<? extends Iterable<Test>> spinalTypeRef = valueTypeRef;
final ArrayList<Iterable<Test>> a2 = new ArrayList<>();
final Iterable<Iterable<Test>> r2 = a2;
}
}
In the first example with a1
, you can see the valid references we can form, first generalizing the value type, and then generating the container's type (the "spine", when talking about a sequence). The second example (a2
) shows how starting with a value type of Iterable
allows the reference type you'd like to form without trouble.
Your question boils down to why valueTypeRef
can't be of type ArrayList<Iterable<Test>>
, and why we need to use an upper-bounded wildcard instead. In Java, type ArrayList<ArrayList<Test>>
has no relationship with type ArrayList<Iterable<Test>>
, even though ArrayList<Test>>
is a subtype of Iterable<Test>
. Forming the valueTypeRef
looks for this would-be relationship for its implicit cast, but no such relationship exists, even if you as the programmer can see that it should exist.
The only way that Java allows you to take advantage of this subtype relationship is with wildcards, as explained here in Wildcards and Subtyping in The Java Tutorial. We need to introduce an upper-bounded wildcard (here, ArrayList<? extends Iterable<Test>>
) to tell the type system that we expect our new reference's value type to be a subtype of its referent's value type.
Type parameters without a wildcard are neither covariant nor contravariant; even if function parameters and return types allow the normal substitutions along subtype and supertype axes, the generic types themselves (e.g. ArrayList<ArrayList<Test>>
and ArrayList<Iterable<Test>>
) can only participate in these substitutions with wildcards.
The resulting type (again, ArrayList<? extends Iterable<Test>>
) precludes use of any mutating functions that would insert some value into the list that's indeed an Iterable<Test>
but not an ArrayList<Test>
. The resulting reference type is covariant with its referent, meaning loosely that it's safe to read through, but not safe to write through.
>` instead? That has an `add` method. For the `Iterable` case, there's semantically no issue (because `Iterable`s are read-only), but for the general case, it allows you to violate the inner template's type, so Java won't allow you to do it.
– Colonel Thirty Two Oct 10 '14 at 16:08