在阅读了Sun关于泛型的文档之后,我转到了http://docs.oracle.com/javase/tutorial/java/generics/QandE/generics-questions.html上的Q和E部分.
对于Q8 -
编写一个通用方法来查找列表[begin,end]范围内的最大元素.
我写的代码是:
private static> T max(List l, int i, int j) { List 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 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的版本比我的更好吗?
编辑:我想比较2个解决方案的效率主要是基于方法声明中使用的类型参数/边界而不是逻辑,例如在什么情况下Sun的版本对于函数的调用者更好?基本上我不明白为什么你需要
和
List extends T>
太阳使用的,而不是我用过的东西.
一个例子非常受欢迎,因为我已经被理论所压倒.(对不起,如果听起来很粗鲁,但我不是故意的).
提前谢谢,穆斯塔法
这里有两个单独的泛型问题.
也就是说,为什么解决方案使用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
该返回的版本Object
.如果使用此备用,不兼容的声明对JDK运行,max
则会返回唯一的版本Comparable
,导致在NoSuchMethodError
链接时抛出.
因此,使用<T extends Object & Comparable<T>>
它实际上是为了控制此声明的擦除,这是由二进制兼容性考虑因素驱动的.在这一点上,该教程似乎有些误导.Collections.max
Java SE中的实际声明是这种二进制兼容性的方式.但是如果你是第一次宣布这种方法,我不认为以这种方式使用交集类型是有用的.
也就是说,而不是:
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>
.它可能List<? extends T>
用于文档目的,以指示仅从列表中检索元素,并且不修改列表.(有关这方面的更多信息,请参阅泛型章节中的Bloch的Effective Java,他讨论了PECS - "Producer Extends,Consumer Super";或Naftalin和Wadler的Java Generics and Collections,他们讨论了"Put and Get Principle".)
链接:
为什么T在Collections.max()签名中被Object绑定?
Angelika Langer的FAQ条目
Java Generics:什么是PECS?
为什么我们需要在Collections.max()方法中使用有界wilcard