为什么我们需要有界wilcard?延伸T>在Collections.max()方法中 [英] Why do we need bounded wilcard <? extends T> in Collections.max() method
问题描述
我读过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);
}
当然,超类和超类都不是很聪明,但它让我们看到实际推断出什么类型的返回值(与以前一样, 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)); //不会编译
我不确定哪里或哪些人可能更喜欢在这种情况下使用显式类型参数,但至少显示了 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); //没有转换为
或传递给接受 我不知道这是否有帮助,但为了说明编译器允许/推断的类型,签名看起来像这样(当然不是这样编译的): 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: And it's mostly the same as the one of I understand why we need bounded wildcard in I've written the following silly example of using both signatures and don't see any diferrence: So can you suggest an example where such simplified signature will be inacceptable? P.S. And why the heck there is Well, thanks to @Bohemian I've managed to figure out what's the difference between them. Consider the following two auxiliary methods 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 ( For both methods inferred type was Sometimes you want be explicit about type passed to overloaded function. You may do it a couple of ways: You can cast: Still no difference... Or you can tell compiler what type should be inferred using syntax Aha! Here is the answer. I'm not sure where or who may prefer to use explicit type parameter in such case, but at least it shows where the And thanks to @erickson and @tom-hawtin-tackline for answers about purpose of 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: Using the signature you've provided: we can code this without errors, warnings or (importantly) casts: And if you need a or to pass to a method that accepts
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):
这篇关于为什么我们需要有界wilcard?延伸T>在Collections.max()方法中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! Middle
(推断不起作用)
pre $ someGenericMethodThatExpectsGenericBoundedToMiddle(MyClass。< Middle> max(list)) ;
public static< Middle extends Comparable< Top>>中等最大值(列表< Bottom>列表)
public static <T extends Comparable<? super T>> T max(List<? extends T> list)
Collections#max
function from standard library.public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
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)
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
in official implementation?Answer
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
is List<ColoredPoint>
as before).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>
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>
. wrongMin
may be inappropriate.T extends Object
constraint.class Top {
}
class Middle extends 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)
List<Bottom> list;
Middle max = max(list); // T inferred to be Middle
Middle
result, without inference, you can explicitly type the call to Middle
: Comparable<Top> max = MyClass.<Middle>max(list); // No cast
Middle
(where inference won't work)someGenericMethodThatExpectsGenericBoundedToMiddle(MyClass.<Middle>max(list));
public static <Middle extends Comparable<Top>> Middle max(List<Bottom> list)