Gábor is correct. The wildcard allows the static type of the returned object to differ from the declared parameter type of the collection you input. For example, given these classes:
interface S extends Comparable<S> {}
class A implements S {
@Override
public int compareTo(final @NotNull S o) {
return 0;
}
}
class B implements S {
@Override
public int compareTo(final @NotNull S o) {
return 0;
}
}
And this class:
class Test {
@Nullable
static <T extends Comparable<? super T>> T extendsMax(
Collection<? extends T> coll) {
return null;
}
@Nullable
static <T extends Comparable<? super T>> T max(Collection<T> coll) {
return null;
}
}
Observe what calls compile and what calls do not:
public static void main(String[] args) {
final Collection<S> sColl = new ArrayList<>();
final Collection<A> aColl = new ArrayList<>();
final Collection<B> bColl = new ArrayList<>();
final S s1 = Test.<S> extendsMax(sColl); // compiles, T = S, <? extends T> = S
final S s2 = Test.<S> extendsMax(aColl); // compiles, T = S, <? extends T> = A
final S s3 = Test.<S> extendsMax(bColl); // compiles, T = S, <? extends T> = B
final A a1 = Test.<A> extendsMax(aColl); // compiles, T = A
final B b1 = Test.<B> extendsMax(bColl); // compiles, T = B
final S s4 = Test.<S> max(sColl); // compiles, T = S
final S s5 = Test.<S> max(aColl); // does not compile, T = S, T != A
final S s6 = Test.<S> max(bColl); // does not compile, T = S, T != B
final S s7 = Test.max(aColl); // compiles, but because T = A, and A
// can be assigned to S
}
So the wildcard allows for some flexibility. While you can omit the wildcard (and to be honest, I can't think of a place off the top of my head where the wildcard is required), there is a reason it is there.
Tom is also incorrect. You can add null to collections with a wildcard (if the collection supports add() in the first place):
List<? extends Number> list = new ArrayList<>();
list.add(null); // compiles, and should execute just fine
And because add(), remove(), and most other mutators in the Collection interface are optional operations, it wouldn't be safe to mutate the collection anyways through those methods if the parameter is just declared as a Collection. In addition, it's generally possible to use iterator().remove() or something of the sort to remove elements from collections regardless of whether they were declared with a wildcard, especially for the ones already included in the Java Collections Framework.
So while a wildcard does limit what you can do with a collection, it should not be used as a way to prevent changes to a collection.