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,?>>
Guidelines for Wildcards. Upper bound wildcard − If a variable is of in category, use extends keyword with wildcard. Lower bound wildcard − If a variable is of out category, use super keyword with wildcard. Unbounded wildcard − If a variable can be accessed using Object class method then use an unbound wildcard.
In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type.
The question mark (?) is known as the wildcard in generic programming. It represents an unknown type. The wildcard can be used in a variety of situations such as the type of a parameter, field, or local variable; sometimes as a return type.
Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.
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:
X<A>
and X<B>
are never assignable unless A = B
. I.e., generics are invariant by default.X<A>
:
X<?>
X<? extends T>
iff A
is assignable to T
(apply rules recursively to A
and T
)X<? super T>
iff T
is assignable to A
(apply rules recursively to T
and A
)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.
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.
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!
Each level of generic nesting either needs to be completely identical (A=B
) or B
needs to contain a wildcard in this level.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With