阅读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 anotation)。
对我而言,实施的保存和简单方法是:
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);
}
}
这是完全安全的。
我的问题是:
答案 0 :(得分:0)
这是一种权衡。 JDK开发人员接受了警告,以减少其他开发人员的警告。如果您使用List
编译旧代码,即作为原始类型,则可以将其传递给Collections.reverse()
,而不会发出警告,因为它是安全的。但是,如果Collections.reverse()
有public static <E> void reverse(List<E> list)
中的类型参数,则在尝试使用原始类型List
调用时会收到警告。
答案 1 :(得分:0)
FWIW我在Generics Faq上找到了这个答案:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ302A
答案 2 :(得分: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库开发人员之外没有人应该关心它;在类型擦除之后,代码与安全代码完全相同,因此二进制文件没有区别。而编译代码的签名和效率都会影响库的用户。