Sink
is a simple example of this sort.
interface Sink<T> { flush(T t); }
writeAll()
is designed to flush all elements of the collection
coll
to the sink snk
, and return the last element
flushed.
public static <T> T writeAll(Collection<T> coll, Sink<T> snk) { T last; for (T t : coll) { last = t; snk.flush(last); } return last; } ... Sink<Object> s; Collection<String> cs; String str = writeAll(cs, s); // Illegal call.
writeAll()
is illegal, as no valid type
argument can be inferred; neither String
nor Object
are
appropriate types for T
, because the Collection
element and the Sink
element must be of the same type.
We can fix this error by modifying the signature of writeAll()
as
shown below, using a wildcard.
public static <T> T writeAll(Collection<? extends T>, Sink<T>) {...} ... String str = writeAll(cs, s); // Call is OK, but wrong return type.
Object
because T
matches the element type of s
, which
is Object
.
The solution is to use
a form of bounded wildcard we haven't seen yet: wildcards with a
lower bound. The syntax ? super T
denotes an unknown type
that is a supertype of T
(or T
itself; remember that
the supertype relation is reflexive). It is the dual of the bounded
wildcards we've been using, where we use ? extends T
to
denote an unknown type that is a subtype of T
.
public static <T> T writeAll(Collection<T> coll, Sink<? super T> snk) { ... } String str = writeAll(cs, s); // Yes!
String
, as desired.
Now let's turn to a more realistic example. A java.util.TreeSet<E>
represents a tree of elements of type E
that are
ordered. One way to construct a
TreeSet
is to pass a Comparator
object to the constructor.
That comparator will be used to sort the elements of the TreeSet
according to
a desired ordering.
TreeSet(Comparator<E> c)
Comparator
interface is essentially:
interface Comparator<T> { int compare(T fst, T snd); }
TreeSet<String>
and pass in a suitable
comparator, We need to pass it a Comparator
that can compare
String
s.
This can be done by a Comparator<String>
, but a
Comparator<Object>
will do just as well. However, we won't be able
to invoke the constructor given above on a Comparator<Object>
.
We can use a lower bounded wildcard to get the flexibility we want:
TreeSet(Comparator<? super E> c)
As a final example of using lower bounded wildcards, lets look at
the method Collections.max()
, which returns the maximal element
in a collection passed to it as an argument.
Now, in order for max()
to work, all elements of the collection
being passed in must implement Comparable
. Furthermore,
they must all be comparable to each other.
A first attempt at generifying this method signature yields:
public static <T extends Comparable<T>> T max(Collection<T> coll)
T
that is comparable
to itself, and returns an element of that type.
However, this code turns out to be too restrictive.
To see why, consider a type that is comparable to arbitrary objects:
class Foo implements Comparable<Object> { ... } Collection<Foo> cf = ... ; Collections.max(cf); // Should work.
cf
is comparable to every other element in cf
,
since every such element is a Foo
, which is comparable to any
object, and in particular to another Foo
. However, using the
signature above, we find that the call is rejected. The inferred type
must be Foo
, but Foo
does not
implement Comparable<Foo>
.
It isn't necessary that T
be comparable
to exactly itself. All that's required is that T
be comparable to
one of its supertypes. This give us:
public static <T extends Comparable<? super T>> T max(Collection<T> coll)
Collections.max()
is more involved. We return
to it in the next section,
Converting Legacy Code to Use Generics.
This reasoning applies to almost any usage of Comparable
that
is intended to work for arbitrary types:
You always want to use Comparable<? super T>
.
In general, if you have an API that only uses a type parameter T
as an argument,
its uses should take advantage of lower bounded wildcards (? super T
).
Conversely, if
the API only returns T
, you'll give your clients
more flexibility by using
upper bounded wildcards (? extends T
).
Set<?> unknownSet = new HashSet<String>(); ... /** Add an element t to a Set s. */ public static <T> void addToSet(Set<T> s, T t) { ... }
addToSet(unknownSet, "abc"); // Illegal.
Now, consider the following code:
class Collections { ... <T> public static Set<T> unmodifiableSet(Set<T> set) { ... } } ... Set<?> s = Collections.unmodifiableSet(unknownSet); // This works! Why?
unmodifiableSet()
does work for any
kind of Set
, regardless of its element type.
Because this situation arises relatively frequently, there is a special rule that allows such code under very specific circumstances in which the code can be proven to be safe. This rule, known as wildcard capture, allows the compiler to infer the unknown type of a wildcard as a type argument to a generic method.