Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning to multilevel wildcards

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,?>>
like image 730
Łukasz Avatar asked Jul 14 '14 13:07

Łukasz


People also ask

How do you use generic wildcards?

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.

Why do we use wildcards in Java?

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.

What is the wildcard character in Java?

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.

Can wildcard types be replaced by any 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.


1 Answers

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:

  1. Two generic types X<A> and X<B> are never assignable unless A = B. I.e., generics are invariant by default.
  2. 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.

like image 96
gexicide Avatar answered Oct 08 '22 21:10

gexicide