Start of Tutorial > Start of Trail > Start of Lesson |
Search
Feedback Form |
In this section, we'll consider some of the more advanced uses of wildcards. We've seen several examples where bounded wildcards were useful when reading from a data structure. Now consider the inverse, a write-only data structure. The interfaceSink
is a simple example of this sort.We can imagine using it as demonstrated by the code below. The methodinterface Sink<T> { flush(T t); }writeAll()
is designed to flush all elements of the collectioncoll
to the sinksnk
, and return the last element flushed.As written, the call topublic 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; neitherString
norObject
are appropriate types forT
, because theCollection
element and theSink
element must be of the same type.We can fix this error by modifying the signature of
writeAll()
as shown below, using a wildcard.The call is now legal, but the assignment is erroneous, since the return type inferred ispublic static <T> T writeAll(Collection<? extends T>, Sink<T>) {...} ... String str = writeAll(cs, s); // Call is OK, but wrong return type.Object
becauseT
matches the element type ofs
, which isObject
.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 ofT
(orT
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 ofT
.Using this syntax, the call is legal, and the inferred type ispublic 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 typeE
that are ordered. One way to construct aTreeSet
is to pass aComparator
object to the constructor. That comparator will be used to sort the elements of theTreeSet
according to a desired ordering.TheTreeSet(Comparator<E> c)Comparator
interface is essentially:Suppose we want to create ainterface Comparator<T> { int compare(T fst, T snd); }TreeSet<String>
and pass in a suitable comparator, We need to pass it aComparator
that can compareString
s. This can be done by aComparator<String>
, but aComparator<Object>
will do just as well. However, we won't be able to invoke the constructor given above on aComparator<Object>
. We can use a lower bounded wildcard to get the flexibility we want:This code allows any applicable comparator to be used.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 formax()
to work, all elements of the collection being passed in must implementComparable
. Furthermore, they must all be comparable to each other.A first attempt at generifying this method signature yields:
That is, the method takes a collection of some typepublic 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:Every element ofclass Foo implements Comparable<Object> { ... } Collection<Foo> cf = ... ; Collections.max(cf); // Should work.cf
is comparable to every other element incf
, since every such element is aFoo
, which is comparable to any object, and in particular to anotherFoo
. However, using the signature above, we find that the call is rejected. The inferred type must beFoo
, butFoo
does not implementComparable<Foo>
.It isn't necessary that
T
be comparable to exactly itself. All that's required is thatT
be comparable to one of its supertypes. This give us:Note that the actual signature ofpublic 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 ofComparable
that is intended to work for arbitrary types: You always want to useComparable<? 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 returnsT
, you'll give your clients more flexibility by using upper bounded wildcards (? extends T
).Wildcard Capture
It should be pretty clear by now that given:The call below is illegal.Set<?> unknownSet = new HashSet<String>(); ... /** Add an element t to a Set s. */ public static <T> void addToSet(Set<T> s, T t) { ... }It makes no difference that the actual set being passed is a set of strings; what matters is that the expression being passed as an argument is a set of an unknown type, which cannot be guaranteed to be a set of strings, or of any type in particular.addToSet(unknownSet, "abc"); // Illegal.Now, consider the following code:
It seems this should not be allowed; yet, looking at this specific call, it is perfectly safe to permit it. After all,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 ofSet
, 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.
Start of Tutorial > Start of Trail > Start of Lesson |
Search
Feedback Form |
Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.