如何编写一个通用的方法来查找最大元素并调用该方法?

9

当我尝试解决泛型教程问答中的练习时,我的答案略有不同。

我的答案

public static <T extends Comparable<? super T>>
    T max(List<? extends T> list, int begin, int end) //Option1

public static <T extends Comparable<T>>
    T max(List<? extends T> list, int begin, int end) //Option2

从下面的引用回答中:

我的问题是

  • 选项1:如果将T extends Object & Comparable<? super T>替换为T extends Comparable<? super T>,是否会有任何区别?extends Object隐含了吗?

  • 选项2:如果将Comparable<? super T>替换为Comparable<T>,是否会有任何区别?如果有,如何处理?

  • Eclipse代码完成在Ctrl+1上创建本地变量List<? extends Comparable<? super Comparable<? super T>>> list;,这有点冗长。如何定义继承Comparable<? super T>的类(层次结构),创建列表并将实例添加到列表中,并调用下面的方法?基本上,我想知道如何在将A或B类实例添加到列表后调用max()方法,其中class B extends A


编写一个通用方法,以查找列表范围 [begin,end)中的最大元素。

答案:

import java.util.*;

public final class Algorithm {
    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;
    }
}

2
部分回答在这里:https://dev59.com/zmkv5IYBdhLWcg3wzj41 - Andrey Adamovich
我不是很理解你的第三个问题。你能详细说明一下吗? - Rohit Jain
Eclipse 会为哪个方法调用创建那个局部变量?我不明白它为什么要这样做。至少在这里嵌套 Comparable 泛型类型真的没有道理。 - Rohit Jain
我想我已经回答了你的第三个问题。你可以在那里检查ParentChild的代码。我猜那就是你想要的。 - Rohit Jain
@RohitJain 当你在Eclipse中输入max(list, 1, 10);并按下Ctrl+1并选择“创建本地变量列表”时,它会被插入。是的,你已经回答了第三个问题,谢谢:)。当Eclipse创建那个冗长的声明时,我有点困惑。 - Prashant Bhate
2个回答

8
如果用 Comparable<T> 替换 Comparable<? super T>,会有什么区别吗?如果有的话,是怎样的?请记住,Comparables始终是消费者,即Comparable<T>消耗T实例,因此始终应该优先使用Comparable<? super T>而不是Comparable<T>(引用自PECS)。 如果您要比较一个其超类实现了Comparable<SuperType>的类型,那么这样做就会有所不同。考虑以下代码:
class Parent implements Comparable<Parent> {
    protected String name;

    @Override
    public int compareTo(Parent o) {
        return this.name.compareTo(o.name);
    }
}

class Child extends Parent {
    public Child(String name) {
        this.name = name;
    }
}

现在,如果你把类型参数设置为T extends Comparable<T>,那么你就不能调用那个方法来处理List<Child>,因为Child没有实现Comparable<Child>而是实现了Comparable<Parent>
public static <T extends Comparable<T>> T max(List<? extends T> list, int begin, int end) {
    ...
}

public static void main(String[] args) {
    List<Child> list = new ArrayList<Child>();
    max(list, 0, 2);  // Error with current method. Child does not implement Comparable<Child>
}

因此,类型参数边界应为 T extends Comparable<? super T>
请注意,您不能将您的Child类更改为:
class Child extends Parent implements Comparable<Child>

因为在这种情况下,Child类将从同一通用类型的不同实例扩展,这是不允许的。

如果用T extends Comparable<? super T>替换T extends Object & Comparable<? super T>,会有什么区别吗?难道extends Object是隐含的吗?

好的,这两个限定之间存在差异。在第一个限定中,类型参数的擦除是Object,而在第二个限定中,擦除是Comparable

因此,如果没有Object限定,您的代码将编译为:

public static Comparable max(List list, int begin, int end)

问题可能出现在您将遗留的非泛型代码泛型化时。为了避免破坏字节码兼容性,必须将 Object 作为上界。您可以在此链接中阅读更多信息:Angelika Langer - Programming Idioms

方法的擦除只对输入参数类型有影响,而不是返回类型,并且Object绑定在这里没有任何区别:它仍然是max(List, int, int)。在这里,Object是真正不必要的。 - Louis Wasserman
@LouisWasserman。嗯,在第一眼看上去,我也是这么想的,但是后来我读了我在末尾添加的链接。它说明这对于保持字节码兼容性是必要的。 - Rohit Jain
@RohitJain 实际上,max(list, 0, 2); 可以编译通过。但是 Child child = max(list, 0, 2); 会编译失败。 - Kachna

0

看起来通配符比必要的多。我可能会选择

public static <T extends Comparable<? super T>>
    T max(List<T> list, int begin, int end)

一个更加受限制的版本:
public static <T extends C, C extends Comparable<C>>
    T max(List<T> list, int begin, int end)

T 必须有一个与 自身 可比较的超类型。例如,T 不能是 Foo

    class Foo implements Comparable<Object>

无论如何,Foo都没有意义;Comparable只能有意义地与自己的同类进行比较。子句 C extends Comparable<C> 承认了这一事实。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接