Nar*_*hai 9 java generics wildcard
这是签名解释的Collections.max()后续问题,其中接受的答案并未涉及此通配符的实际原因.
该max方法需要一个Collection<? extends T>,我无法想到这个通配符有用的实际案例.
通常,如果您的API只使用类型参数T作为参数,则其使用应该利用较低的有界通配符(?super T).相反,如果API仅返回T,您将通过使用上限有通配符(?extends T)为您的客户提供更大的灵活性.
但我仍然没有得到它.甚至Java Generics and Collections一书也没有说明这个通配符背后的原因.
这有实际用途吗?一个真实世界的用例会很棒.
想象一下这两个签名:
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);
Run Code Online (Sandbox Code Playgroud)
现在,让我们尝试构造类型的函数.
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);
Run Code Online (Sandbox Code Playgroud)
如您所见,使用显式类型,其中一种方法不再起作用.
当然,您可以省略泛型类型参数的显式绑定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);
Run Code Online (Sandbox Code Playgroud)
甚至:
Function<Collection<Timestamp>, Date> f2 = Test::max1;
Function<Collection<Timestamp>, Date> f3 = Test::max2;
Run Code Online (Sandbox Code Playgroud)
并且类型推断将发挥其神奇作用,因为以上所有编译.
正如pbabcdefp在他的回答中所说,我们应该总是对API的一致性感到烦恼.想象一下,还有一个额外的optionalMax()方法(只是为了处理空参数集合的情况):
static <T extends ...> Optional<T> optionalMax1(Collection<T> coll);
static <T extends ...> Optional<T> optionalMax2(Collection<? extends T> coll);
Run Code Online (Sandbox Code Playgroud)
现在,您可以清楚地看到只有第二个变体是有用的:
// 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;
Run Code Online (Sandbox Code Playgroud)
考虑到这一点,以下API会感觉非常不一致,因而错误:
static <T extends ...> T max(Collection<T> coll);
static <T extends ...> Optional<T> optionalMax(Collection<? extends T> coll);
Run Code Online (Sandbox Code Playgroud)
因此,为了保持一致(无论是否需要输入),方法应该使用Collection<? extends T>签名.总是.
一个有用的实验是这样的:
static <T extends Object & Comparable<? super T>> T max1(Collection<T> coll) {
return max2(coll);
}
static <T extends Object & Comparable<? super T>> T max2(Collection<? extends T> coll) {
return max1(coll);
}
Run Code Online (Sandbox Code Playgroud)
编译的事实表明这两个签名接受完全相同的参数集。
事实上,Collections类的签名中有几个多余的通配符。
例如,不需要通配符
static <T> void fill(List<? super T> list, T t)
Run Code Online (Sandbox Code Playgroud)
和
static <T> void copy(List<? super T> dst, List<? extends T> src)
Run Code Online (Sandbox Code Playgroud)
如果一个或另一个(但不是两个)通配符不存在,也会同样有效。
那么为什么要包括它们呢?
这个copy例子是一个很好的例子。super强调的使用dst是在只读的意义上使用的,extends强调的使用src是在只读的意义上使用的。同样fill,在 中super,虽然不需要,但它强调list正在写入,但未读取。在 中max,extends强调正在List读取但未写入。对于Collection既可以读取又可以写入的方法,例如
static <T extends Comparable<? super T>> void sort(List<T> list)
Run Code Online (Sandbox Code Playgroud)
不使用通配符。
总而言之,我认为通配符没有任何实际用途,但它是指示该Collection方法使用 的方式的好方法。