|
Permitting a Broader Argument Range
Employ three types of J2SE 1.5's wildcards as type arguments of parameterized types while recognizing their limitations
by Klaus Kreft and Angelika Langer
Posted May 19, 2004
Let's explore typical use of the different types of wildcards. We already saw a typical example for using a wildcard instantiation—see Part 1, "Wildcard Instantiations of Parameterized Types" (Java Pro Online, May 12, 2004)—in which we discussed the drawAllShapes() method. If we want the drawAllShapes() method to accept not only collections of shapes but also collections of subtypes of Shape, then we can achieve this result by declaring the argument as a Collection<? extends Shape>.
public void drawAllShapes(
Collection<? extends Shape>
c) {
for (Shape s : c) {
s.draw();
}
}
Collection<Triangle> c =
new LinkedList<Triangle>();
... fill the collection with
triangles ...
drawAllShapes(c);
Essentially, the wildcard is used to relax the requirements imposed on the method arguments and to permit a broader range of argument types.
Programmers familiar with parameterized methods might have noticed that the same effect can be achieved without wildcards as well: if we declare the drawAllShapes() method as a parameterized method, we would also allow collections of subtypes of Shape as arguments. The parameterization would look like this:
public <T extends Shape> void
drawAllShapes(
Collection<T> c) {
for (Shape s : c) {
s.draw();
}
}
The parameterized drawAllShapes() method accepts collections whose element type is a subtype of Shape, exactly as the nonparameterized version with the wildcard instantiation does. The difference is that the parameterized version does not restrict access to the method argument, while the wildcard instantiations disallow the invocation of methods that take arguments of the unknown type. In this example, it does not make any difference because the only method we invoke on the Shape type (or subtype thereof) is the draw () method, which does not take any arguments. In another context the parameterized method might be more flexible than its nonparameterized counterpart with the wildcard argument. The point to take home is: wildcards with an upper bound can often be replaced by parameterization with a bounded type parameter.
Using Unbounded Wildcards
Unbounded wildcards are used when the type of the type parameter does not matter, either because the object referred to is not used or because no methods of that type are invoked. A method that prints all elements in a collection serves as an example of using an unbounded wildcard:
void printAll(Collection<?> c) {
for (Object o : c)
System.out.println(
o.toString());
}
All that is required of the elements in the collection is that they must have a toString() method. Since the toString() method is defined in the Object class, this requirement is not significant. Basically, we need not know anything about the type of the objects contained in the collection. Hence, a declaration as Collection<?> is appropriate.
Back to top
|