Assigning to multilevel wildcards

bounded-wildcard, generics, java

Solution

I will try to explain the Java generics using two simple rules. These rules suffice to answer your question and are basically enough to remember for almost any case:

- Two generic types `X<A>` and `X<B>` are never assignable unless `A = B`. I.e., generics are invariant by default.

- Wildcards allow the assignment of `X<A>`:

- to `X<?>`

- to `X<? extends T>` iff `A` is assignable to `T` (apply rules recursively to `A` and `T`)

- to `X<? super T>` iff `T` is assignable to `A` (apply rules recursively to `T` and `A`)

Case `c3 = c1`

In your example, you try to assign `Collection<Pair<String,Long>>` to `Collection<Pair<String,?>>`. That is, in your case `A = Pair<String,Long>` and `B = Pair<String,?>`. Since these types are not equal, they are not assignable; they violate Rule 1.

The question is, why doesn't the wildcard help? The answer is simple: Rule 2 is NOT transitive. I.e., `X<X<A>>` cannot be assinged to `X<X<?>>`, there has to be a wildcard in the outermost level; otherwise Rule 2 does not apply to the outermost level.

Case `c4 = c1`

Here, you got a wildcard in the outer type. Because it is in the outer type, Rule 2 kicks in: `A = Pair<String,?>` is assignable to `B = ? extends Pair<String,Long>` (again, because of Rule 2). Therefore, this is legal.

General approach

Here is how you can check any complex generic type: Simply check each generic level by level using the two rules. Start with the outermost level. Once a level violates a rules, you know the assignment is illegal; if all levels adhere to the rules, then the assignment is legal. Lets consider your types again:

X = Collection<Pair<String,Long>>
Y = Collection<Pair<String,?>>
Z = Collection<? extends Pair<String,?>> 

Is X assignable to Y ?

// Outermost level:
A = Pair<String,Long>, B = Pair<String,?>
  => B is no wildcard and A != B (Rule 1), so this is illegal!

Is X assignable to Z ?

// Outermost level:
A = Pair<String,Long>, B = ? extends Pair<String,?>
  => We got a wildcard, so Rule 2 states this is legal if the inner level is legal
// Inner level: (we have to check both parameters)
A = String, B = String => Equal, Rule 1 applies, fine!
A = Long, B = ? => B is wildcard, Rule 2 applies, fine!

Simple Rule to Remember

Each level of generic nesting either needs to be completely identical (`A=B`) or `B` needs to contain a wildcard in this level.

Problem

Simple class: ``` class Pair<K,V> { } ``` And a few assignments: ``` Collection<Pair<String,Long>> c1 = new ArrayList<Pair<String,Long>>(); Collection<Pair<String,Long>> c2 = c1; // ok Collection<Pair<String,?>> c3 = c1; // this does not compile Collection<? extends Pair<String,?>> c4 = c1; // ok ``` why does bullet number three not compile while the fourth one is perfectly legal? Compiler error: ``` Type mismatch: cannot convert from Collection<Pair<String,Long>> to Collection<Pair<String,?>> ```

Original source

Related problems