让我们假设我们有
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)
也适用!我的问题是为什么。有人可以向我解释吗?谢谢:)
答案 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)?