> draft
Zhong Yu, 2015-09-01
This is a compilation of wildcard use cases for the article Capturing Wildcards
Contents
The classic example of capture helper is reverse(List<?>)
, as shown in JLS
void reverse(List<?> list) { // difficult to work with wild type // forward it to helper method rev(list); } <T> void rev(List<T> listT) { // read from and write back to `listT` // which is easy on a concrete type ... }
Given a (Map<?,?> map)
, we can iterate its entries like
for(Entry<?,?> entry : map.entrySet())
This is deceptively simple, but we know it's more complex under the hood. First, map
is capture converted to Map<X1, X2>
, therefore, entrySet()
returns Set<Entry<X1, X2>>
. The entry
variable can be declared as Entry<?,?>
, because it's a supertype of Entry<X1, X2>
.
Following that example, someone may try this --
Set<Entry<?,?>> entrySet = map.entrySet(); // compile error
it doesn't work because Set<Entry<X1, X2>>
is not a subtype of Set<Entry<?,?>>
.
We can add more wildcards to solve the problem, see Nested Wildcards and Subtyping.
Set<? extends Entry<?,?>> entrySet = map.entrySet(); Iterator<? extends Entry<?,?>> iterator = entrySet.iterator();
But it is becoming too messy. A simpler way is to use a capture helper to do away with wildcards --
void foo(Map<?,?> map) { foo2(map); } <K,V> void foo2(Map<K,V> map) { Set<Entry<K,V>> entrySet = map.entrySet(); Iterator<Entry<K,V>> iterator = entrySet.iterator(); ... }
Source - http://stackoverflow.com/questions/16753587
The compiler often needs to infer a common supertype from two or more subtypes.
For example, the type of a conditional expression (without target typing)
condition ? expr1 : expr2
is a common supertype of expr1
and expr2
. For Exception
and Error
, the supertype is inferred as Throwable
.
If the subtypes are parameterized types, the supertype may be inferred as a wild type. For example,
if expr1
is List<Exception>
, and expr2
is List<Error>
, the type of the conditional expression is inferred as
List<? extends Throwable>
(which is then immediately capture converted).
The algorithm to infer the supertype is Least Upper Bound which is very complicated, and may yield a very complicated wild type. However in most use cases, the programmer doesn't need to know the exact outcome of the algorithm.
Let's see another example, if we invoke the method Arrays.asList
public static <T> List<T> asList(T... a)
with two arguments, one is Set<Exception>
and another is Set<Error>
, T
will be inferred as
Set<? extends Throwable>
, and the method will return List<Set<? extends Throwable>>
.
What are the supertypes of java.lang.Integer
?
From the class definition, Integer
inherits Object, Number, Serializable, Comparable<Integer>
; these are all supertypes of Integer
. Integer
is also a supertype of itself.
But that's not all. Comparable<Integer>
has supertypes that are wild types. In fact
Comparable<Integer> <: Comparable<? extends A> iff Integer<:A
Therefore, for every supertype of Integer
, we can construct another supertype, and so on.
It is also the case that
Comparable<Integer> <: Comparable<? super B> iff B<:Integer
however, since Integer
is final
, B
can only be Integer
itself.
Therefore the following examples are all supertypes of Integer
Comparable<? extends Integer> Comparable<? extends Comparable<? extends Number>> Comparable<? extends Comparable<? extends Comparable<? super Integer>>>
This is a contrived example demonstrating pervasive capture conversions
List<?> list = ...; ( condition ? list : list ).get(0);
The two list
sub-expressions are capture converted first, to List<X1>
and List<X2>
respectively. The type of the parent conditional expression is then derived as the least upper bound of these two concrete types, which is List<? extends Object>
. This type is then capture converted to List<X3>
, therefore get(0)
returns X3
.
Capture conversion is applied everywhere. This may surprise the programmer sometimes, because the type of an expression may not be what it seems to be. For example, the following code does not compile
Class<?> c1 = ...; Class<?> c2 = ...; Collections.singleton(c1).add(c2); // compile error
Collections.<T>singleton
takes a T
argument and returns a Set<T>
. Naively, for (Class<?> c1)
argument, we may expect that the method returns Set<Class<?>>
, therefore add(c2)
should work.
But actually, the c1
argument undergoes capture conversion first, changing its type to Class<X1>
. Therefore T
is inferred as Class<X1>
, and singleton(c1)
returns Set<Class<X1>>
. The add()
method then has the signature add(Class<X1>)
, which does not accept the c2
argument, which has the type (after capture) Class<X2>
.
There is no way to suppress capture conversion here. A workaround is to explicitly specify T→Class<?>
Collections.<Class<?>>singleton(c1).add(c2); // compiles! (but fails at runtime:)
See also - http://stackoverflow.com/questions/30991884 (Mockito method chaining)
Capture conversion only affects wild types; on any other type, the type is left as is. For example
<T> void rev(List<T> list){ ... } <L extends List<?>> void test(L list) { #a rev( (List<?>)list ); // ok #b rev( list ); // compile error }
Line#a
compiles, because the argument type is List<?>
, which is capture converted to List<X>
, and type inference concludes that T=X
for rev()
. See Capture Helper
Line#b
does not compile, because the argument type is L
, which is not subject to capture conversion, even though it is-a List<?>
. Type inference then fails -- no T
can be found so that L <: List<T>
.
This is a curious example where a subtype (L
) cannot be used where a supertype (List<?>
) works; an upcast is required. This might be an oversight of the spec and may be fixed in future.
Source - http://stackoverflow.com/a/30624513/2158288
In most cases, capture conversion is pretty simple, like List<?> ⇒c List<X>
.
However, it gets a little more complicated for more complex type parameters. The classic example is Enum<E>
class Enum< E extends Enum<E> >
The bound of E
references itself. During capture conversion, the bound of E
needs to be substituted too
Enum<? extends Runnable> ⇒c Enum<X> where X <: Runnable & Enum<X>
Subtyping analysis requires capture conversion on wild types. For example, given this generic declaration
interface Data<T> extends List<List<T>>
To analyze subtyping between wild types of Data
and List
Data<? extends Number> ⇒c Data<X> where X<:Number Data<X> <: List<List<X>> <: List<? extends List<X>> <: List<? extends List<? extends Number>> Data<? extends Number> <: List<? extends List<? extends Number>>
Note that Data<?>
is not a List<List<?>>
, see Nested Wildcard.
Suppose for some reason we have to cast a Supplier<Integer>
to Supplier<Number>
, which we know is safe at runtime because of type erasure. Direct casting is forbidden --
Supplier<Integer> supplier = ...; (Supplier<Number>)supplier // error!
which makes perfect sense because the two types are mutually exclusive, therefore casting is provably incorrect.
But we want to force the cast anyway; this can be done by up-casting to a common supertype first, followed by down-casting. Obviously, we can always use Object
as the supertype between any two types
(Apple)(Object)orange (Supplier<Number>)(Object)supplier
but it's probably a good practice to use a supertype that's closer to the two types
(Supplier<Number>)(Supplier<? extends Number>)supplier
If that's too ugly, maybe use an unbounded wildcard
(Supplier<Number>)(Supplier<?>)supplier
This is about on-site casting. For other ways of conversions, see Missing Wildcards.
It is not uncommon that a method signature forgets to include proper wildcards. Such mistakes exist even in core Java APIs which were designed under a lot of scrutiny. Interestingly, it seldom causes any problems to API users; and, when problems do arise, there are simple workarounds ( requires Java 8 ).
This observation can be used to justify deliberately omitting wildcards in APIs, to escape from Wildcard Hell.
Consider the following method signature which lacks proper wildcards
void foo(Function<String, Number> func)
we won't be able to pass compatible functions to it
Function<CharSequence, Integer> f = ...; foo( f ); // error!
However, it usually isn't a problem, because in most use cases, the argument is a lambda expression or method reference, and compiler type inference takes care of providing the desired target type
Integer str2int(CharSequence chars){ ... } ... foo( str->str2int(str) ); // ok! foo( this::str2int ); // ok!
If the argument is an expression of a fixed static type, in majority cases it happens to be the right type -- the target type.
In rare cases, like foo(f)
, the argument type doesn't match, and we need to convert it. On-site casting is possible, but way too ugly. For functional interfaces, conversion can be done simply by method reference
Function<CharSequence, Integer> f = ...; foo( f ); // error! foo( f::apply ); // ok!
However, the purpose of f::apply
might be unclear to readers of the code. Personally, I'd like to use a help method to make the intent clear that I'm doing a variant cast --
foo( vary(f) ); ... @SuppressWarnings("unchecked") public static <T,R> Function<T,R> vary(Function<? super T, ? extends R> f0) { return (Function<T,R>)f0; // erasure }
The implementation of vary()
depends on type erasure; it is perfectly safe at runtime, because method f0.apply()
indeed accepts any T
and returns an R
, therefore f0
can be safely used as a Function<T,R>
.
Similarly, we can define a vary()
method for each functional interface that's intuitively variant, like Consumer
etc.
For interfaces like List
, we'll need two helper methods, one for covariant cast, and one for contravariant.
public static <T> List<T> covary(List<? extends T> list) { return (List<T>)list; } double sum(List<Number> list) // no wildcard! // usage List<Integer> intList = ...; sum( covary(intList) );
If we are sure that sum()
wants a list of Number
in a covariant sense, we can safely pass it a List<Integer>
this way.
Occasionally we may encounter types like List<? extends String>
, with a wildcard upper-bounded by a final
class. It's unlikely that the programmer voluntarily constructs a type like that. Most likely it's imposed by a List<? extends T>
in some generic API which happens to be substituted with T→String
.
List<? extends String>
seems odd. Since String
is a final
class, the only subtype is String
itself. Therefore it's tempting to think that List<? extends String>
is the same as List<String>
.
However, as far as the compiler is concerned, they are two distinct types. This is because final
is not final; it is possible that someday String
class removes the final
modifier, and new subclasses of String
are introduced.
Therefore, List<? extends String> ≠ List<String>
; in particular, this subtyping is false
List<? extends String> <: List<String>
Although, as a practical matter, if we have to, we may cast List<? extends String>
to List<String>
, reasoning that it must be safe at runtime, as long as String
stays final
.
On the other hand, what about List<? super Object>
? The only supertype of Object
is Object
itself, therefore, aren't List<? super Object>
and List<Object>
the same type?
If two types are subtypes of each other, they are the same type, or at least, they are semantically equivalent. So the question is whether the subtyping algorithm agrees that
List<? super Object> <: List<Object>
which depends on whether the subtyping is true on the capture conversion
List<X> <: List<Object> for any X that Object<:X
The reasonable answer is yes. And javac
seems to agree that List<? super Object> = List<Object>
. For example the following code compiles without any problem
void test( Set< List<? super Object> > x, Set< List<Object> > y ) { x = y; y = x; }
We have the following subtyping property
• G<? extends B> <: G<? extends A> if B<:A
However, the converse may not be true, because we also have to consider the bound of the type-parameter.
For example, consider this generic declaration
class Foo<T extends Exception>
The following wild types are all equivalent, and they are subtypes of each other
Foo<? extends Object> Foo<? extends Throwable> Foo<? extends Serializable> Foo<? extends Exception>
because, the wildcard bounds provide no more restriction than the type-parameter bound. During capture conversion,
Exception = Exception&Throwable = Exception&Serializable
In essence, what's really important to a wild type is its capture conversion, not its syntactic form.