Variations on Generics - Wildcards
In the documentation for a collections class, you may see some strange type
parameters for methods or constructors, such as:
public boolean containsAll(Collection<?> c)
public boolean addAll(Collection<? extends E> c)
public TreeSet(Comparator<? super E> comparator)
The question mark is a wildcard, indicating that the actual type
is unknown. But, we at least know limits in the second two cases. The
in this usage actually means "is, extends, or implements" (which
is the same criteria the
instanceof operator applies). The
means essentially the opposite: that the type parameter of the other class
is, or is more basic than, this class's type. The usages with
super are called bounded wildcards.
This syntax only occurs when the variable is itself a
generic class. The wildcards then state how that class's generic type
relates to this class's type.
Why this is necessary leads down a long and winding path. To start, consider
List<Employee> exEmps = new ArrayList<ExemptEmployee>();
This seems reasonable at first glance, but then consider if this line followed:
Perfectly legal as far as the compiler is concerned, since
ContractEmploye fits within the
Employee type that the
exEmps variable requires, but now we have a contract employee in a list instance that is supposed to hold only exempt employees. So, an
instance of a class parameterized with a derived class is not an instance of that class parameterized with the base class, even though individual instances of the derived class can be used in the base-parameterized generic class; e.g., our
List<Employee> can add individual exempt employees.
For the first wildcard case above,
public boolean containsAll(Collection<?> c), it does no harm for us to see if our collection contains all the elements of some other collection that may contain an entirely unrelated type (but few, if any, of the items would compare as equal). Note that the
contains method accepts an
Object parameter, not an
E, for this same reason.
extends term in
public boolean addAll(Collection<? extends E> c)means that the unknown class is, extends, or implements the
listed type. For instance, we could add all the elements of an
ArrayList<Employee>. That makes sense, since we could add individual exempt employees to a basic employee collection. But, since we don't actually know what the parameterized type of the incoming collection is (it is the
? class), we cannot call any methods on that object that depend on its parameterized type. So we can't add to that collection; we can only read from it.
super term is seen less often; it means that the parameterized type of the incoming collection must be of the same type or a more basic type. The
TreeSet constructor can accept a
Comparator for its actual type, or any type more basic (e.g., a
Comparator<Object> can be used for a
TreeSet of anything, since its
compare method will accept any type of data). It is, however, likely that many "acceptable" comparators will end up throwing a
ClassCastException at runtime if they can't actually compare the types involved. So, for example, if we had a
Comparator<Employee> class that compared employee ids, we might still wish to use it in a
TreeSet<ExemptEmployee>, where it would be perfectly valid (in fact, it would be an annoyance to have to write a special comparator for every employee type, if all the comparators did was compare the ids).