> draft

Wildcard Case Studies

Zhong Yu, 2015-09-01

This is a compilation of wildcard use cases for the article Capturing Wildcards

Contents

Capture Helper

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
    ...
}

Map<?,?> Entry Set

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

Inferred Wild Type

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>>.

Supertypes of Integer

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>>>

Capture Everywhere

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.

Unexpected Capture

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)

Unexpected Non-Capture

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

Capture Conversion

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

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.

Casting Between Concrete Types

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.

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.

Tight Bound

? extends String

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.

? super Object

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;
}

Loose Bound

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.