1

阅读 openjdk7 的一些代码,我发现方法 Collections.reverse 实现为(我已经删除了对 RandomAccess 列表的一些优化):

public static void reverse(List<?> list) {
    int size = list.size();
    ListIterator fwd = list.listIterator();
    ListIterator rev = list.listIterator(size);
    for (int i = 0, mid = list.size() >> 1; i < mid; i++) {
        Object tmp = fwd.next();
        fwd.set(rev.previous());
        rev.set(tmp);
    }
}

ListIterators 的两个初始化都会生成未经检查的警告(并且代码中没有 @SupressWarnings 注释)。

对我来说,保存和简单的实现方法是:

public static <E> void reverse2(List<E> list) {
    int size = list.size();
    ListIterator<E> fwd = list.listIterator();
    ListIterator<E> rev = list.listIterator(size);
    for (int i = 0, mid = list.size() >> 1; i < mid; i++) {
        E tmp = fwd.next();
        fwd.set(rev.previous());
        rev.set(tmp);
    }
}

这是完全安全的。

我的问题是:

  • 为什么 openjdk 使用原始类型的不安全代码?
  • 为什么警告没有被抑制?
4

3 回答 3

0

从概念上讲,reverse()只需要一个List任意参数。它根本不关心,也不需要参数与其他类型相关。因此,void reverse(List<?> list)是最简单(因此也是最好)的签名。

现在,要在泛型中正确地写它,在 的实现内部reverse,需要使用列表的类型参数,因为我们需要从列表中取出一个元素,并将其放回,我们需要一个类型来表达这个临时值。

在 Java 中做到这一点的唯一方法是为整个方法声明一个泛型参数——使其成为泛型方法——就像你reverse2做的那样:<E> void reverse2(List<E> list)

但是,这会更改签名(外部可见)。这里E只在参数中的一处使用,从类型的角度来看基本上是不必要的。我们需要在E内部使用的事实是外部代码不需要关心的实现细节。

有一种方法可以使用完全安全的结构并保留签名void reverse(List<?> list):使用捕获助手。即创建一个辅助方法,List<?>它所做的只是调用泛型方法(然后可以将其设为私有):

public static void reverse(List<?> list) {
    reverse2(list);
}

(如果你不明白为什么在泛型下这是正确的,提示:这是由于捕获。)

尽管这似乎是两全其美(更简单的签名 + 安全构造),但缺点是,从运行时的角度来看,这是一种浪费。我们有一个采用一个参数的方法,它所做的只是将它直接传递给另一个采用一个参数的方法。这似乎完全无关紧要。类型擦除后 - 您将使用一种方法List调用另一种方法List。(如果我向您展示了这样一个函数,而您不了解泛型,您可能会立即认为它没有必要。)

因此,为了提高效率,他们决定在内部代码中牺牲完全安全的结构,以保留更简单的签名和单个函数调用的效率,而不是两个。他们使用的结构无论如何都在内部代码中,所以除了 Java 库开发人员之外没有人应该关心它;类型擦除后,代码与安全代码完全相同,因此二进制文件没有区别。而编译代码的签名和效率都会影响库的用户。

于 2013-10-25T00:32:11.327 回答
0

FWIW 我在泛型常见问题解答中找到了这个答案:

http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ302A

于 2013-10-24T10:18:06.500 回答
0

这是一个权衡。JDK 开发人员接受他们一方的警告,以减少对其他开发人员的警告。如果您使用 a 编译旧代码List,即作为原始类型,您可以将其传递给Collections.reverse()而不会发出警告,因为它是安全的。但是如果Collections.reverse()有一个像 in 这样的类型参数,public static <E> void reverse(List<E> list)当你尝试使用 raw type 调用它时会收到一个警告List

于 2013-10-24T08:17:34.810 回答