Start of Tutorial > Start of Trail > Start of Lesson |
Search
Feedback Form |
Consider the problem of writing a routine that prints out all the elements in a collection. Here's how you might write it in an older version of the language (i.e., a pre-5.0 release):And here is a naive attempt at writing it using generics (and the newvoid printCollection(Collection c) { Iterator i = c.iterator(); for (k = 0; k < c.size(); k++) { System.out.println(i.next()); } }for
loop syntax):The problem is that this new version is much less useful than the old one. Whereas the old code could be called with any kind of collection as a parameter, the new code only takesvoid printCollection(Collection<Object> c) { for (Object e : c) { System.out.println(e); } }Collection<Object>
, which, as we've just demonstrated, is not a supertype of all kinds of collections!So what is the supertype of all kinds of collections? It's written
Collection<?>
(pronounced "collection of unknown"), that is, a collection whose element type matches anything. It's called a wildcard type for obvious reasons. We can write:and now, we can call it with any type of collection. Notice that insidevoid printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); } }printCollection()
, we can still read elements fromc
and give them typeObject
. This is always safe, since whatever the actual type of the collection, it does contain objects. It isn't safe to add arbitrary objects to it however:Since we don't know what the element type ofCollection<?> c = new ArrayList<String>(); c.add(new Object()); // Compile time errorc
stands for, we cannot add objects to it. Theadd()
method takes arguments of typeE
, the element type of the collection. When the actual type parameter is?
, it stands for some unknown type. Any parameter we pass toadd
would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception isnull
, which is a member of every type.On the other hand, given a
List<?>
, we can callget()
and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result ofget()
to a variable of typeObject
or pass it as a parameter where the typeObject
is expected.Bounded Wildcards
Consider a simple drawing application that can draw shapes such as rectangles and circles. To represent these shapes within the program, you could define a class hierarchy such as this:These classes can be drawn on a canvas:public abstract class Shape { public abstract void draw(Canvas c); } public class Circle extends Shape { private int x, y, radius; public void draw(Canvas c) { ... } } public class Rectangle extends Shape { private int x, y, width, height; public void draw(Canvas c) { ... } }Any drawing will typically contain a number of shapes. Assuming that they are represented as a list, it would be convenient to have a method inpublic class Canvas { public void draw(Shape s) { s.draw(this); } }Canvas
that draws them all:Now, the type rules say thatpublic void drawAll(List<Shape> shapes) { for (Shape s: shapes) { s.draw(this); } }drawAll()
can only be called on lists of exactlyShape
: it cannot, for instance, be called on aList<Circle>
. That is unfortunate, since all the method does is read shapes from the list, so it could just as well be called on aList<Circle>
. What we really want is for the method to accept a list of any kind of shape:There is a small but very important difference here: we have replaced the typepublic void drawAll(List<? extends Shape> shapes) { ... }List<Shape>
withList<? extends Shape>
. NowdrawAll()
will accept lists of any subclass ofShape
, so we can now call it on aList<Circle>
if we want.
List<? extends Shape>
is an example of a bounded wildcard. The?
stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype ofShape
. (Note: It could beShape
itself, or some subclass; it need not literally extendShape
.) We say thatShape
is the upper bound of the wildcard.There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into
shapes
in the body of the method. For instance, this is not allowed:You should be able to figure out why the code above is disallowed. The type of the second parameter topublic void addRectangle(List<? extends Shape> shapes) { shapes.add(0, new Rectangle()); // Compile-time error! }shapes.add()
is? extends Shape
-- an unknown subtype ofShape
. Since we don't know what type it is, we don't know if it is a supertype ofRectangle
; it might or might not be such a supertype, so it isn't safe to pass aRectangle
there.Bounded wildcards are just what one needs to handle the example of the DMV passing its data to the census bureau. Our example assumes that the data is represented by mapping from names (represented as strings) to people (represented by reference types such as
Person
or its subtypes, such asDriver
).Map<K,V>
is an example of a generic type that takes two type arguments, representing the keys and values of the map.Again, note the naming convention for formal type parameters--
K
for keys andV
for values.public class Census { public static void addRegistry(Map<String, ? extends Person> registry) { } ... Map<String, Driver> allDrivers = ... ; Census.addRegistry(allDrivers);
Start of Tutorial > Start of Trail > Start of Lesson |
Search
Feedback Form |
Copyright 1995-2005 Sun Microsystems, Inc. All rights reserved.