Generics
Wildcards¶
To make up for the lack of variance, Java has a feature called wildcards, in which question marks are used as type arguments. The type LList<?>
represents an object that is an LList<T>
for some type T, though precisely which type T is not known at compile time (or for that matter, even at run time).
A value of type LList<T>
(for any T) can be used as if it had type LList<?>
, so there is a family of subtyping relationships LList<T>
<: LList<?>
. This means that a method can provide a caller with a list of any type without the client knowing what is really stored in the list; the client can get elements from the list but cannot change the list:
LList<?> f() {
LList<Integer> i = new LList();
i.add(2);
i.add(3);
i.add(5);
return i;
}
// in caller
LList<?> lst = f();
lst.add(7); // illegal: type ? not known
for (Object o : lst) {
println(o);
}
Note that the type of the elements iterated over is not really known either, but at least we know that the type hidden by ? is a subtype of Object
. So it is type-safe to declare the variable o
as an Object
.
If we need to know more about the type hidden by the question mark, it is possible to add an extends
clause. For example, suppose we have an interface Animal
with two implementing classes Elephant
and Rhino
. Then the type Collection<? extends Animal>
is a supertype of both Collection<Elephant>
and Collection<Rhino>
, and we can iterate over the collection and extract Animal
s rather than just Object
s.
Collection<? extends Animal> c = new LList<Rhino>();
for (Animal a : c) {
// use a as Animal here
}