馆藏说明.max签名

2022-09-04 03:31:22

当我读到一篇关于Java泛型的文章时,我偶然发现了这种方法签名:

static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll);

我不明白的部分是为什么我们需要有

Collection<? extends T> coll

不会

Collection<T> coll

也这样做吗?

有人可以解释为什么以下签名是不够的吗?

static <T extends Object & Comparable<? super T>> T max(Collection<T> coll);

提前感谢您的回复。这让我困惑了很长一段时间。


答案 1

加博尔是正确的。通配符允许返回对象的静态类型不同于您输入的集合的声明参数类型。例如,给定以下类:

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
}

因此,通配符允许一定的灵活性。虽然你可以省略通配符(说实话,我想不出在我的头顶上有一个地方需要通配符),但它存在是有原因的。


汤姆也是不正确的。您可以使用通配符添加到集合(如果集合首先支持):nulladd()

List<? extends Number> list = new ArrayList<>();
list.add(null); // compiles, and should execute just fine

由于 接口中的 、 和 大多数其他赋值函数都是可选操作,因此,如果参数只是声明为 .,则通过这些方法改变集合是不安全的。此外,通常可以使用或类似的东西从集合中删除元素,无论它们是否使用通配符声明,特别是对于已经包含在Java集合框架中的元素。add()remove()CollectionCollectioniterator().remove()

因此,虽然通配符确实限制了您可以对集合执行的操作,但它不应用作防止对集合进行更改的一种方式。


答案 2

推荐