为什么我们需要有界wilcard?延伸T>在Collections.max()方法中 [英] Why do we need bounded wilcard <? extends T> in Collections.max() method

查看:76
本文介绍了为什么我们需要有界wilcard?延伸T>在Collections.max()方法中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读过Joshua Bloch的Effective Java。但书中的一个例子对我而言并不清楚。它来自关于泛型的章节,确切的项目是项目28:使用有界的通配符来增加API的灵活性在这个项目中显示如何使用有界的类型参数和有界的通配符类型来编写从集合中选择最大元素的算法的最普遍和防弹(在类型系统观点)版本。



所写的静态方法的最终签名如下所示:

  public static< T extends Comparable< ;? super T>> T max(List<?extends T> list)

它与<
$ b

  public static< T extends对象&可比< ;? super T>> T max(Collection< ;? extends T> coll)

我明白为什么我们需要在 T扩展了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 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,颜色);
}
}

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(集合<?扩展T> xs){
迭代器<延伸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(
新的ColoredPoint(1,2,Color.BLACK),
新的ColoredPoint(0,2,Color.BLUE),
新的ColoredPoint(0,-1, Color.RED)
);
Point p1 = wrongMin(points);
Point p2 = min(points);
System.out.println(最小元素是+ p1);
}

那么你能否提出一个例子,这种简化的签名是不可接受的? p>

PS为什么在官方实现中存在 T extends Object
$ b

Answer



好吧,感谢@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)); // printOverloaded for ColoredPoint
expectsPointOrColoredPoint(wrongMin(points)); // printOverloaded for ColoredPoint

对于这两种方法推断的类型都是 ColoredPoint

有时候你想明确传入重载函数的类型。您可以通过以下几种方式进行操作:

您可以投射:

  expectPointOrColoredPoint((Point)min(points)); //打印重载为Point
期望PointOrColoredPoint((Point)wrongMin(points)); // printOverloaded for Point

仍然没有区别......



或者你可以告诉编译器应该使用语法 class。< type>方法 p>

  expectsPointOrColoredPoint(Algorithms。< Point> min(points)); // printOverloaded for Point
expectsPointOrColoredPoint(Algorithms。< Point> wrongMin(points)); //不会编译



Aha!这是答案。因为泛型不是协变的(不同于数组),所以 List< ColoredPoint> 无法传递给期望 Collection< Point> ),但可以传递给期望 Collection <?>的函数。扩展Point>



我不确定哪里或哪些人可能更喜欢在这种情况下使用显式类型参数,但至少显示了 wrongMin 可能不合适。



感谢@erickson和@ tom-hawtin-tackline,以获得有关 T的用途的答案extends Object 约束。

解决方案

区别在于返回的类型,尤其受到 / em>,其中类型可以是在类型和List类型之间的层次结构类型。让我举个例子:

  class Top {
}
class Middle扩展Top implements Comparable< Top> ; {
@Override
public int compareTo(Top o){
//
}
}
class Bottom extends Middle {
}

使用您提供的签名:

  public static< T extends Comparable< ;? super T>> T max(List< ;? extends T> list)

我们可以在没有错误,警告或重要的是)演员:

 列表< Bottom>列表; 
Middle max = max(list); // T推断为中间

如果您需要 a Middle 结果,无需推断,您可以明确地将调用输入到 Middle

 可比< Top> max = MyClass。< Middle> max(list); //没有转换为

或传递给接受 Middle (推断不起作用)

pre $ someGenericMethodThatExpectsGenericBoundedToMiddle(MyClass。< Middle> max(list)) ;






我不知道这是否有帮助,但为了说明编译器允许/推断的类型,签名看起来像这样(当然不是这样编译的):

  public static< Middle extends Comparable< Top>>中等最大值(列表< Bottom>列表)


I've read awesome "Effective Java" by Joshua Bloch. But one example in the books is left unclear to me. It's taken from chapter about generics, exact item is "Item 28: Use bounded wildcards to increase API flexibility".

In this item it's shown how to write the most universal and bulletproof (at the type system point of view) version of the algorithm of selection maximum element from collection using bounded type parameters and bounded wildcard types.

The final signature of the static method written looks like this:

public static <T extends Comparable<? super T>> T max(List<? extends T> list)

And it's mostly the same as the one of Collections#max function from standard library.

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 

I understand why we need bounded wildcard in T extends Comparable<? super T> type constraint, but is it really necessary in type of the argument? It seems to me that it will be the same if we leave just List<T> or Collection<T>, isn't it? I mean something like this:

public static <T extends Comparable<? super T>> T wrongMin(Collection<T> xs)

I've written the following silly example of using both signatures and don't see any diferrence:

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);
    }

So can you suggest an example where such simplified signature will be inacceptable?

P.S. And why the heck there is T extends Object in official implementation?

Answer

Well, thanks to @Bohemian I've managed to figure out what's the difference between them.

Consider the following two auxiliary methods

private static void expectsPointOrColoredPoint(Point p) {
    System.out.println("Overloaded for Point");
}

private static void expectsPointOrColoredPoint(ColoredPoint p) {
    System.out.println("Overloaded for ColoredPoint");
}

Sure, it's not very smart to overload method both for superclass and its subclass, but it let us see what type of return value was actually inferred (points is List<ColoredPoint> as before).

expectsPointOrColoredPoint(min(points));     // print "Overloaded for ColoredPoint"
expectsPointOrColoredPoint(wrongMin(points)); // print "Overloaded for ColoredPoint"

For both methods inferred type was ColoredPoint.

Sometimes you want be explicit about type passed to overloaded function. You may do it a couple of ways:

You can cast:

expectsPointOrColoredPoint((Point) min(points));     // print "Overloaded for Point"
expectsPointOrColoredPoint((Point) wrongMin(points)); // print "Overloaded for Point"

Still no difference...

Or you can tell compiler what type should be inferred using syntax class.<type>method:

expectsPointOrColoredPoint(Algorithms.<Point>min(points));     // print "Overloaded for Point"
expectsPointOrColoredPoint(Algorithms.<Point>wrongMin(points)); // will not compile

Aha! Here is the answer. List<ColoredPoint> can't be passed to function expecting Collection<Point> because generics are not covariant (unlike arrays), but can be passed to function expecting Collection<? extends Point>.

I'm not sure where or who may prefer to use explicit type parameter in such case, but at least it shows where the wrongMin may be inappropriate.

And thanks to @erickson and @tom-hawtin-tackline for answers about purpose of T extends Object constraint.

解决方案

The difference is in the type returned, especially influenced by inference, whereby the type may be a type hierarchically between the Comparable type and the List type. Let me give an example:

class Top {
}
class Middle extends Top implements Comparable<Top> {
    @Override
    public int compareTo(Top o) {
        // 
    }
}
class Bottom extends Middle {
}

Using the signature you've provided:

public static <T extends Comparable<? super T>> T max(List<? extends T> list)

we can code this without errors, warnings or (importantly) casts:

List<Bottom> list;
Middle max = max(list); // T inferred to be Middle

And if you need a Middle result, without inference, you can explicitly type the call to Middle:

 Comparable<Top> max = MyClass.<Middle>max(list); // No cast

or to pass to a method that accepts Middle (where inference won't work)

someGenericMethodThatExpectsGenericBoundedToMiddle(MyClass.<Middle>max(list));


I don't know if this helps, but to illustrate the types the compiler as allowed/inferred, the signature would look like this (not that this compiles, of course):

public static <Middle extends Comparable<Top>> Middle max(List<Bottom> list)

这篇关于为什么我们需要有界wilcard?延伸T&gt;在Collections.max()方法中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆