想象一下这两个签名:
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);
现在,让我们尝试构造函数类型。
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);
如您所见,使用显式类型,其中一种方法不再起作用。
类型推断在“现实世界”中“隐藏”了这个问题:
当然,你可以省略泛型类型参数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);
甚至:
Function<Collection<Timestamp>, Date> f2 = Test::max1;
Function<Collection<Timestamp>, Date> f3 = Test::max2;
并且类型推断会发挥它的魔力,因为上述所有内容都可以编译。
现在何必呢?
我们应该总是关心 API 的一致性,就像pbabcdefp has said in his answer。想象一下有一个额外的optionalMax() 方法(只是为了处理空参数集合的情况):
static <T extends ...> Optional<T> optionalMax1(Collection<T> coll);
static <T extends ...> Optional<T> optionalMax2(Collection<? extends T> coll);
现在,您可以清楚地看到只有第二个变体有用:
// 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;
考虑到这一点,下面的 API 会感觉非常不一致,因此是错误的:
static <T extends ...> T max(Collection<T> coll);
static <T extends ...> Optional<T> optionalMax(Collection<? extends T> coll);
因此,为了保持一致(无论是否需要输入),方法应该使用Collection<? extends T> 签名。总是。