Imagine these two signatures:
static <T extends Object & Comparable<? super T>> T max1(Collection<T> coll);
static <T extends Object & Comparable<? super T>> T max2(Collection<? extends T> coll);
Now, let's try to construct functions of type.
Function<Collection<java.sql.Timestamp>, java.util.Date>
// This doesn't work:
Function<Collection<Timestamp>, Date> f0 =
(Collection<Timestamp> col) -> Test.<Date>max1(col);
// This works:
Function<Collection<Timestamp>, Date> f1 =
(Collection<Timestamp> col) -> Test.<Date>max2(col);
As you can see, using explicit typing, one of the methods doesn't work any longer.
Type inference "hides" this problem in the "real world":
Of course, you could just omit the explicit binding of the generic type argument Test.<Date>maxN()
:
Function<Collection<Timestamp>, Date> f0 =
(Collection<Timestamp> col) -> Test.max1(col);
Function<Collection<Timestamp>, Date> f1 =
(Collection<Timestamp> col) -> Test.max2(col);
Or even:
Function<Collection<Timestamp>, Date> f2 = Test::max1;
Function<Collection<Timestamp>, Date> f3 = Test::max2;
And type inference would work its magic, as all of the above compile.
Now why bother?
We should always bother about API consistency, as pbabcdefp has said in his answer. Imagine there was an additional optionalMax()
method (just to handle the case of an empty argument collection):
static <T extends ...> Optional<T> optionalMax1(Collection<T> coll);
static <T extends ...> Optional<T> optionalMax2(Collection<? extends T> coll);
Now, you can clearly see that only the second variant is useful:
// Does not work:
Function<Collection<Timestamp>, Optional<Date>> f0 =
(Collection<Timestamp> col) -> Test.optionalMax1(col);
Function<Collection<Timestamp>, Optional<Date>> f2 = Test::max1;
// Works:
Function<Collection<Timestamp>, Optional<Date>> f1 =
(Collection<Timestamp> col) -> Test.optionalMax2(col);
Function<Collection<Timestamp>, Optional<Date>> f3 = Test::optionalMax2;
With this in mind, the following API would feel very inconsistent, and thus wrong:
static <T extends ...> T max(Collection<T> coll);
static <T extends ...> Optional<T> optionalMax(Collection<? extends T> coll);
So, in order to be consistent (no matter if the typing is needed or not), the methods SHOULD be using the Collection<? extends T>
signature. Always.