为什么我们需要有界威尔卡<?在集合.max() 方法中扩展 T>答
我读过Joshua Bloch的“Effective Java”。但书中的一个例子对我来说是不清楚的。它取自关于泛型的章节,确切的项目是“项目28:使用有界通配符来提高API灵活性”。
在本项目中,它展示了如何使用有界类型参数和有界通配符类型编写从集合中选择最大元素的算法的最通用和防弹版本(在类型系统的角度来看)。
写入的静态方法的最终签名如下所示:
public static <T extends Comparable<? super T>> T max(List<? extends T> list)
它与标准库中的函数基本相同。Collections#max
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
我理解为什么我们需要在类型约束中使用有界通配符,但是在参数类型中真的有必要吗?在我看来,如果我们只离开或,情况也会是一样的,不是吗?我的意思是这样的:T extends Comparable<? super T>
List<T>
Collection<T>
public static <T extends Comparable<? super T>> T wrongMin(Collection<T> xs)
我已经写了以下使用两个签名的愚蠢示例,并且没有看到任何差异:
public class Algorithms {
public static class ColoredPoint extends Point {
public final Color color;
public ColoredPoint(int x, int y, Color color) {
super(x, y);
this.color = color;
}
@Override
public String toString() {
return String.format("ColoredPoint(x=%d, y=%d, color=%s)", x, y, color);
}
}
public static class Point implements Comparable<Point> {
public final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return String.format("Point(x=%d, y=%d)", x, y);
}
@Override
public int compareTo(Point p) {
return x != p.x ? x - p.x : y - p.y;
}
}
public static <T extends Comparable<? super T>> T min(Collection<? extends T> xs) {
Iterator<? extends T> iter = xs.iterator();
if (!iter.hasNext()) {
throw new IllegalArgumentException("Collection is empty");
}
T minElem = iter.next();
while (iter.hasNext()) {
T elem = iter.next();
if (elem.compareTo(minElem) < 0) {
minElem = elem;
}
}
return minElem;
}
public static <T extends Comparable<? super T>> T wrongMin(Collection<T> xs) {
return min(xs);
}
public static void main(String[] args) {
List<ColoredPoint> points = Arrays.asList(
new ColoredPoint(1, 2, Color.BLACK),
new ColoredPoint(0, 2, Color.BLUE),
new ColoredPoint(0, -1, Color.RED)
);
Point p1 = wrongMin(points);
Point p2 = min(points);
System.out.println("Minimum element is " + p1);
}
那么,您能举个例子说明这种简化的签名是不可接受的吗?
附言:为什么在官方实施中会有这种情况?T extends Object
答
好吧,多亏了@Bohemian我已经设法弄清楚了它们之间的区别。
考虑以下两种辅助方法
private static void expectsPointOrColoredPoint(Point p) {
System.out.println("Overloaded for Point");
}
private static void expectsPointOrColoredPoint(ColoredPoint p) {
System.out.println("Overloaded for ColoredPoint");
}
当然,为超类及其子类重载方法不是很聪明,但它让我们看看实际上推断出的返回值类型(和以前一样)。points
List<ColoredPoint>
expectsPointOrColoredPoint(min(points)); // print "Overloaded for ColoredPoint"
expectsPointOrColoredPoint(wrongMin(points)); // print "Overloaded for ColoredPoint"
对于这两种方法,推断的类型为 。ColoredPoint
有时,您希望显式地说明传递给重载函数的类型。您可以通过以下几种方式执行此操作:
您可以投射:
expectsPointOrColoredPoint((Point) min(points)); // print "Overloaded for Point"
expectsPointOrColoredPoint((Point) wrongMin(points)); // print "Overloaded for Point"
还是没什么区别...
或者,您可以告诉编译器应使用语法 class.<type>method
推断哪种类型:
expectsPointOrColoredPoint(Algorithms.<Point>min(points)); // print "Overloaded for Point"
expectsPointOrColoredPoint(Algorithms.<Point>wrongMin(points)); // will not compile
啊哈!答案是这样的。 不能传递给期望函数,因为泛型不是协变的(与数组不同),但可以传递给期望的函数。List<ColoredPoint>
Collection<Point>
Collection<? extends Point>
我不确定在这种情况下,在哪里或谁可能更喜欢使用显式类型参数,但至少它显示了可能不合适的地方。wrongMin
感谢@erickson和@tom-hawtin-tackline关于约束目的的答案。T extends Object