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,?>> ```