加博尔是正确的。通配符允许返回对象的静态类型不同于您输入的集合的声明参数类型。例如,给定以下类:
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;
}
}
这个类:
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;
}
}
观察哪些调用编译,哪些调用不调用:
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
}
因此,通配符允许一定的灵活性。虽然你可以省略通配符(说实话,我想不出在我的头顶上有一个地方需要通配符),但它存在是有原因的。
汤姆也是不正确的。您可以使用通配符添加到集合(如果集合首先支持):null
add()
List<? extends Number> list = new ArrayList<>();
list.add(null); // compiles, and should execute just fine
由于 接口中的 、 和 大多数其他赋值函数都是可选操作,因此,如果参数只是声明为 .,则通过这些方法改变集合是不安全的。此外,通常可以使用或类似的东西从集合中删除元素,无论它们是否使用通配符声明,特别是对于已经包含在Java集合框架中的元素。add()
remove()
Collection
Collection
iterator().remove()
因此,虽然通配符确实限制了您可以对集合执行的操作,但它不应用作防止对集合进行更改的一种方式。