Sunday 20 September 2009

Item 28 - Use Bounded Wildcards to Increase API Flexibility

In this item Josh introduces the usage of bounded wildcards. Wildcards were introduced in their unbounded form in Item 23 as a type-safe alternative to raw types for use when you do not know or care what the actual type parameter: List<?> represents a list of some arbitrary type, with greater type safety than List<Object>.

Bounded wildcards allow you to specify a type parameter as an arbitrary subtype or supertype of a specified class. So, List<?extends E> represents a list of some subtype of E, and List<? super E> represents a list of somesupertype of E. (Note that the subtype relation is defined so that Number is a subtype of itself.)

As noted in Item 25, generic types are invariant. Given two type B and
D where D is a subtype B, List<D> is not a subtype of List<B>. This can give rise to unfortunate restrictions. Considering the Stack class previously used as an example - you can invoke push(intVal) on a Stack<Number> where intVal is an Integer, because Integer is a subtype of Number. You can also store the return value of pop() in an Object, because Object is asupertype of Number.

However, extending the Stack class to include methods to push and pop sequences of elements runs into a snag:

public void pushAll(Iterable<E> src) {...}
public void popAll(Collection<E> dst) {...}

These won't do; while they compile, they will only work with collections which exactly match the stack's element type. With our Stack<Number>, passing a sequence of Integers topopAll() will fail, as will passing a sequence of Objects to popAll(). The solution is to use bounded wildcards:

public void pushAll(Iterable<? extends E> src) {...}
public void popAll(Collection<? super E> dst) {...}

The pushAll() method can now be passed a sequence of any type which is
a subtype of the stack's type parameter. Likewise, the argument to pushAll(), which is effectively an out parameter, may now be a sequence of any type which is a supertype of the stack's type parameter. In other words, for a Stack<Number>, you can pop sequences of Integers, and push sequences of Objects.

Josh introduces a mnemonic to remember which type of wildcard type to use: PECS, standing for Producer-Extends, Consumer-Super. This is not the most snappy and memorable mnemonic I've ever heard, but if it helps people then fine. I prefer the Get-Put Principle which Josh refers to but does not quote:

"Use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you do both."

Note that popAll() defines a parameter rather than a return type. This
allows the wildcard types to be as unobtrusive to the user of the class as possible. If a wildcard type was used as a return type, users of the class would be used to wildcard types in their own code.

After demonstrating the use of wildcard types with some of the methods from previous items, Josh rounds off with a neat little technique - though it does not relate specifically to boundedwildcards . Using the example of a swap() method, he demonstrates that the method could be declared using an unbounded type parameter or an unboundedwildcard. The wildcard version clearly presents a cleaner, simpler interface and so should be used in a public API. However the implementation of the type parameter version is much more straightforward. Happily the tension here can be resolved by making the type parameter version a private helper method, to which the public method forwards, with thewildcard type being captured in the process.

Ewan Milne

No comments: