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.