Java通用方法,使用compareTo方法问题从集合中查找“ max”对象

时间:2019-03-15 08:13:43

标签: java generics

让我们假设我们有

class A implements Comparable<A>

还有

class B extends A

现在,我希望有一个通用方法,可以使用compareTo方法从集合中找到最大的对象。

如果我这样声明此方法:

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

它不适用于参数List<B> list,因为B不会将类型Comparable的{​​{1}}参数化。为了使该方法起作用,我似乎很自然地更改

B

收件人

Comparable<T>

因此更改后的方法将如下所示:

Comparable<? super T>

现在传递参数public static <T extends Comparable<? super T>> T max(List<T> list) 可以正常工作,并且不会出错。精细。但是当我将方法签名更改为

List<B> list

它对于参数public static <T extends Comparable<T>> T max(List<? extends T> list) 也适用!我的问题是为什么。有人可以向我解释吗?谢谢:)

2 个答案:

答案 0 :(得分:1)

List<? extends T>的意思是“一个列表。我们不知道列表,但是无论列表是什么,我们都知道它扩展了T或本身就是T。”

这样做的一个效果是,当我们从List中获取一个元素时,编译器将知道它的类型为T。该项的实际运行时类可能是T的子类型,多态性总是如此,但没人在乎。我们知道它可以存储在变量T中。

另一个影响是我们不能在列表中放置任何元素,因为我们不知道列表是什么。因此,无论我们尝试放入哪个内部,我们都不知道它是否属于可接受的类型,因此我们不能。但是,找到最大值时没关系:我们也不会添加任何东西。

因此,如果需要,List<B>也可以当作List<? extends A>使用(突然决定我们不知道不在乎列表是什么,但是无论列表是什么,它都会扩展A 。)这样做很有帮助,因为然后它允许匹配方法签名,以T为A。

答案 1 :(得分:0)

这不是一回事。

在第一个示例中,您的意思是,“对于某些类型的T,我可以将任何T强制转换为a的Comparable T的父类型,那么我将接受T的列表并返回T的实例。”

当您使用List<B>调用方法时,由于每个B都是Comparable<A>,因此可以这样说:“我可以将任何B强制转换为{ Comparable的超类型为{1}},类型参数B解析为具体类型T

在第二个示例中,您说的是:“对于某些类型的B,我可以将任何T转换为可比较的T ,那么我将接受T的任何列表或T的任何子类型的任何列表,并返回T的实例。”

使用A T调用 this 时,类型参数List<B>解析为T;当然,任何A都是A,您可以将Comparable<A>传递给方法,因为List<B>的列表确实是{{1}的子类型的列表}。

这就是为什么两者都可以编译的原因。

但是您在问能够说B的目的。这样一来,您就可以随时接收正在使用的类的子类的集合(或其他参数化类型)。当您要接收某种类型的对象并对其执行某些操作时,通常这就是您想要的,并且通常,您同样乐意对子类型执行此操作(因为子类型应遵循相同的合同)。

A关键字意味着您可以使用类型参数为类型参数超类的对象。典型的情况是当您要将对象添加到集合中时。假设此处的List<? extends T>解析为具体类型super。如果收到T,则B是合法的,因为可以将List<A> myList添加到List.add(B)。如果您的方法返回一个B,这也很有用-在这种情况下,即使集合中的所有内容都是List<A>,调用代码也无法将其分配给Set<T>,但是如果您返回Set<A>,则可以。对于您的情况,您将呼叫A,在这里,类似地,将Set<? super T>的实例传递给Comparable<A>.compare(B)是合法的。

对此有个助记符,这里讨论了PECS“生产者扩展,消费者超级”:What is PECS (Producer Extends Consumer Super)?