在阅读了Sun关于泛型的文档后,我转到了http://docs.oracle.com/javase/tutorial/java/generics/QandE/generics-questions.html的Q和E部分。
对于Q8 -
编写一个通用方法来查找列表[begin,end]范围内的最大元素。
我写的代码是:
private static <T extends Comparable<T>> T max(List<T> l, int i, int j) {
List<T> sublist = l.subList(i, j);
System.out.println("Sublist "+sublist);
int c = 0;T max = null;
for(T elem: sublist) {
if(c == 0 || max.compareTo(elem) < 0) {
max = elem;
} ++c;
}
return max;
}
和Sun的回答是:
public static <T extends Object & Comparable<? super T>>
T max(List<? extends T> list, int begin, int end) {
T maxElem = list.get(begin);
for (++begin; begin < end; ++begin)
if (maxElem.compareTo(list.get(begin)) < 0)
maxElem = list.get(begin);
return maxElem;
}
有人可以告诉我一个例子,Sun的版本比我的更好吗?
编辑:我想比较两种解决方案的效率,主要是基于方法声明中使用的类型参数/边界而不是逻辑,例如在什么情况下Sun的版本更适合函数的调用者?基本上我不明白你为什么需要Sun使用的<T extends Object & Comparable<? super T>
和List<? extends T>
而不是我使用过的。
我非常感激一个例子,因为我已被理论所压倒。 (对不起,如果这听起来很粗鲁,但我不是故意的。)
提前致谢, 穆斯塔法
答案 0 :(得分:7)
这里有两个独立的仿制药问题。
也就是说,为什么解决方案使用T extends Object & Comparable<T>
而不是更简单的T extends Comparable<T>
? (我在本节中省略了通配符;我将在下面介绍它们。)
我不认为Object & Comparable<T>
与类型系统中的Comparable<T>
有任何不同,因为每个对象都延伸Object
。也就是说,没有类型T
扩展Comparable<T>
,但不会扩展Object & Comparable<T>
。
但是有一点不同。交叉点类型擦除到交集的第一个组件,因此T extends Object & Comparable<T>
将删除到Object
,而T extends Comparable<T>
将删除到Comparable
。考虑max
方法的两个替代声明:
<T extends Comparable<T>> T max1(List<T> list) { ... }
<T extends Object & Comparable<T>> T max2(List<T> list) { ... }
如果使用javap -s
转储这些方法的签名,您可以看到显示已删除类型的内部签名:
<T extends java/lang/Comparable<T>> T max1(java.util.List<T>);
Signature: (Ljava/util/List;)Ljava/lang/Comparable;
<T extends java/lang/Object & java/lang/Comparable<T>> T max2(java.util.List<T>);
Signature: (Ljava/util/List;)Ljava/lang/Object;
谁在乎擦除类型? JVM呢。 JVM基于匹配参数类型和返回类型来查找方法。因此,擦除的返回类型可能很重要。
事实上,从二进制兼容性的角度来看,它具有重要意义。在Java SE 5之前,当引入泛型时,声明了Collections.max
方法并且删除了签名,如下所示:
public static Object max(Collection coll)
Signature: (Ljava/util/Collection;)Ljava/lang/Object;
在Java SE 5及更高版本中,声明和删除的签名是:
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
Signature: (Ljava/util/Collection;)Ljava/lang/Object;
至关重要的是,删除的签名相同。
如果没有使用交集类型声明Java SE 5声明,它将如下所示:
public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll)
Signature: (Ljava/util/Collection;)Ljava/lang/Comparable;
这将是二进制不兼容。针对旧版JDK编译的二进制文件将引用返回max
的{{1}}版本。如果使用此备用,不兼容的声明针对JDK运行,则Object
的唯一版本将返回max
,导致在链接时抛出Comparable
。
因此,使用NoSuchMethodError
实际上是为了控制此声明的擦除,这是由二进制兼容性考虑因素驱动的。在这一点上,该教程似乎有些误导。 Java SE中<T extends Object & Comparable<T>>
的实际声明是这种二进制兼容性的方式。但如果你是第一次宣布这种方法,我不认为以这种方式使用交集类型是有用的。
即代替:
Collections.max
为什么使用通配符:
static <T extends Comparable<T>> T max(List<T> list)
这里,通配符对于在存在子类型时更灵活地使用该方法是必要的。请考虑以下事项:
static <T extends Comparable<? super T>> T max(List<? extends T> list)
如果使用了非通配符声明,则不会匹配class A implements Comparable<A> { ... }
class B extends A { }
List<B> bList = ...;
B bMax = max(bList);
。为了完成这项工作,T
是必要的。这样可以将Comparable<? super T>
推断为T
,一切正常。
我必须承认,我无法找到一个示例,说明在这种情况下需要B
的原因。如果参数仅被声明为List<? extends T>
,则此示例可以正常工作。可能是List<T>
用于文档目的,以指示仅从列表中检索元素,并且不修改列表。 (有关这方面的更多信息,请参阅泛型章节中的Bloch的 Effective Java ,他将讨论PECS - “Producer Extends,Consumer Super”;或Naftalin和Wadler的 Java Generics and Collections 他们讨论“放弃和获取原则”。)
链接:
Why is T bound by Object in the Collections.max() signature?