If we define a type like Collection
interface Collection
We can then plug in different things for E
to get different types: for example, Collection<Animal>
has methods Animal get()
and void add(Animal e)
If we want to write generic code that works on any Collection
of animals, we can't simply use Collection<Animal>
, because a Collection<Dog>
is not a Collection<Animal>
. That's because we can call add(new Cat())
on a Collection<Animal>
, but we can't on a Collection<Dog>
.
Wildcards give us a common supertype that we can use for arguments to methods that should be able to operate on multiple kinds of collections.
Collection<? extends Animal>
is a supertype of each type Collection<T>
where T
is a subtype of Animal
. See board image for which methods are allowed.
Collection<? super Animal>
is a supertype of each type Collection<T>
where T
is a supertype of Animal
. See board image for which methods are allowed.
The way to think about subtype relationships between wildcard types is by thinking about what methods exist in a wildcard type, and then seeing if the proposed subtype methods are more specific than the proposed supertype.
For example, Collection<? extends Animal>
has methods Animal get()
but no add
method at all. Collection<Dog>
has a get
method; it returns a Dog
and a Dog
is-an Animal
, so this satisfies the specification of Collection<? extends Animal>
so a Collection<Dog>
is-a Collection<? extends Animal>
.