Generics and Limitations in Java - Wildcards (Page 4 of 5 )
We mentioned earlier that the kinds of generic type instantiations discussed so far in this chapter have all been concrete type instantiations. We described this as meaning that all of the parameter arguments are real Java types. For example, List<String> and List<Date> are instantiations of the generic List class with the concrete types String and Date. Now we're going to look at another kind of generic type instantiation: wildcard instantiation.
As we'll see in this section, wildcards are Java's way of introducing polymorphism into the type parameter portion of the generic equation. A wildcard instantiation uses a question mark (?) in place of an actual type parameter at instantiation time and denotes that the type can be assigned any of a range of possible instantiations of the generic type. The ? wildcard by itself is called the unbounded wildcard and denotes that any type instantiation is acceptable (assignable to the type).
List<?> anyInstantiationOfList = new ArrayList<Date>(); anyInstantiationOfList = new ArrayList<String>(); // another instantiation
In this snippet, we declared a variable anyInstantiationOfList whose type is the unbounded wildcard instantiation of the generic List type. (What a mouthful.) This means that the type we instantiated can be assigned any particular concrete instantiation of the List type, whether Dates, Strings, or Foos. Here, we assigned it a List<Date> first and, subsequently, a List<String>.
A Supertype of All Instantiations
The unbounded wildcard instantiation is a kind of supertype of all of these concrete instantiations. In contrast to the generic type relationships that we saw earlier, which followed only raw, "base" generic types, wildcards let us implement polymorphism on the parameter types. The unbounded wildcard is to generic type parameters what the Object type is to regular Java types: a supertype of everything.
// A List<Object> is nota List<Date>! List<Object> objectList = new ArrayList<Date>() // Error!
// A List<?> can be a List<date> List<?> anyList = new ArrayList<Date>(); // Yes!
We are reminded in this example that List<Object> is not a List<Date>; polymorphism doesn't flow that way with generic instantiations of concrete types. But List<?>, the unbounded wildcard instantiation, can be assigned any instantiation of List. As we go on, we'll see that wildcards add a new dimension to the assignability of generic types.
A bounded wildcard is a wildcard that uses the extends keyword just as a type variable would, to limit the range of assignable types. For example:
List<? extends Date> dateInstantiations = new ArrayList<Date>(); dateInstantiations = new ArrayList<MyDate>(); // another instantiation
Our dateInstantiations variable is limited to holding instantiations of List on parameter types of Date and its subclasses. So, we can assign it a List<Date> or a List<MyDate>. In the same way that the unbounded wildcard serves as a superclass for all instantiations of a generic type, bounded wildcards create more limited supertypes covering a narrower range of instantiations. In this case, our wildcard instantiation, List<? extends Date>, is the supertype of all instantiations of List on Date types. As with type parameter bounds, the bound Date is called the upper bound of the type.
Wildcard bounds may extend interfaces as well as use the & syntax to add interface requirements to the bound:
Trap< ? extends Catchable & Releaseable > trap;
In this case, the instantiation serves as a supertype of the set of instantiations on types implementing both the Catchable and Releaseable interfaces.