Generics aren't covariant. In other words, a Collection<String>
is not a Collection<Object>
, even though a String
is an Object
. With the first signature, I wouldn't be able print a List<String>
for example.
Generic wildcards assist in the expression of covariance (as in your example) and contravariance. Collection<?>
is short for Collection<? extends Object>
and means "a collection of some specific, unknown type that is or extends Object
". As a consequence, we wouldn't be able to add anything but null
to such a collection since we couldn't guarantee the added object's type would be valid.
Here's an example that uses a wildcard to express contravariance:
void populateCollection(Collection<? super Integer> c) {
for (int i = 0; i < 10; i++) {
c.add(i);
}
}
I could pass a Collection<Object>
, Collection<Number>
, or Collection<Integer>
into this method, since it's treated as a consumer of Integer
s. In your example, the collection is a producer of Object
s. See this post for further reading on the distinction between "producers" and "consumers" with respect to wildcarded generic types: What is PECS (Producer Extends Consumer Super)?