Java 中的泛型

Generics in Java

我正在尝试学习 Java 中的泛型编程概念,但我坚持了一个 question.Consider 函数 max 的这些实现:

1)

public static <T extends Comparable<? super T>> T max(Collection<? extends T> collection){
    //code
}

2)

public static <T extends Comparable<T>> T max(Collection<T> collection){
 //code
}

我很想知道,它们之间有什么区别?

当然,我知道,声明 Collection<? extends T> 允许将 T 的子类型作为 Collection 传递,但是在这种情况下,在静态方法中它有什么用?

不使用边界结果必须相同,不是吗?

那这个 <T extends Comparable<? super T>> 呢?

如果你传递T的子类型,反正没问题,因为T实现了Comparable,Comparable的实现会在子类型中维护,所以你可以使用2)选项。

我只是想知道是否有一些情况,当不可能使用 2) 选项,但可以使用 1)

这适用于第一个版本,但不适用于第二个版本:

class A implements Comparable<A> {...};
class B extends A {...};

List<B> list = new ArrayList<>();

B maxItem = max(list);

在这两种情况下,类型参数 T 都设置为 B,因为 listCollection<B>。然而,B 没有实现 Comparable<B>,因此它不能满足第二个声明中对 T extends Comparable<T> 的约束。

不过,它适用于第一个声明,因为 AB 的超类。

这解释了 Comparable<? super T>... Collection<? extends T> 在这里并不是真正必要的——我认为 max 方法没有任何理由成为 return 类型除了集合元素类型 T,所以 Collection<T> 就可以了。

要完全理解它,您缺少两个版本。这是全部四个:

public static <T extends Comparable<? super T>> T max1(Collection<? extends T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}
public static <T extends Comparable<T>> T max2(Collection<T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}
public static <T extends Comparable<T>> T max3(Collection<? extends T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}
public static <T extends Comparable<? super T>> T max4(Collection<T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}

为了看看它们是如何工作的,我会偷 :

class A implements Comparable<A> {
    @Override public int compareTo(A that) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
}
class B extends A {
}

现在让我们看看四种 max 方法中哪一种有效:

List<B> list = new ArrayList<>();

B maxItem1  = max1(list);         // T = B (but could be A if result is A, see next line)
A maxItem1a = Test.<A>max1(list); // T = A
B maxItem1b = Test.<B>max1(list); // T = B

B maxItem2  = max2(list);         // compile error

A maxItem3  = max3(list);         // T = A (forced by Comparable<T>)

B maxItem4  = max4(list);         // T = B (forced by Collection<T>)

如您所见,对于 max3()max4(),由于缺少通配符,T 被强制为特定类型。

max2()根本不行,因为没有T可以同时满足Comparable<T>Collection<T>

至于max1()T可以解析为AB,但如果解析为A,则必须赋值成为 A 类型的变量。如果解析为 B,分配可以是 AB
如果不手动指定T类型,编译器会选择B.


请注意,由于您的 max 方法可能只会迭代集合一次,您甚至可以将参数类型更改为 Iterable,一切仍将正常工作。

这将允许您找到非集合迭代的最大值,例如 java.nio.file.Path 以在文件夹 中查找最后一个文件(按字典顺序).

让我们把它分解成几个部分:

<T extends Comparable<? super T>>

这定义了 T 的泛型边界——它可以表示任何 Comparable 类型——因为 TComparable 或者因为某些父 class 是。由于该父 class(称之为 P)可能实现 Comparable<P> 而不是 Comparable<T>,因此有必要在此处使用 super。听起来你已经知道了。

T

这是方法的 return 类型,通常可以由调用上下文指定(例如,当您将结果分配给变量时)。我们会回到这个。

Collection<? extends T>

这允许您传入 TCollectionT 的任何子类型(称之为 S)。如果您刚刚指定 Collection<T>,您将 能够传递 Collection<T> 个对象而不是 Collection<S> 个对象。

将它们放在一起可以让调用者执行如下操作:

// notice that max() returns a C, but is assignable to a B
B b = max(new ArrayList<C>());

其中 ABC 是:

A implements Comparable<A>
B extends A
C extends B

事实证明,由于您的方法 return 直接是 T,因此 Collection<? extends T> 并不重要,因为您始终可以分配子类型(C) 到父类型 (B)。但是,如果我们将方法签名稍微调整为 return 一个使用 T 作为泛型的类型,这将不起作用:

public static <T extends Comparable<? super T>> Collection<T>
    collect(Collection<T> collection)

注意我们现在 returning a Collection<T> - 现在 ? extends 变得很重要 - 这不会编译:

Collection<B> b = collect(new ArrayList<C>());

而如果您将参数更改为 Collection<? extends T>,则会放宽界限; return 类型不必与参数类型相同。您可以传入 Collection<C> 但因为调用者使 T 表示 B (因为这是 return 类型所期望的类型) C 现在 扩展 T,而不是 T.

简而言之,对于您的方法,我认为您不需要 ? extends T,但在某些情况下您需要。更一般地说,如果你打算在你的泛型中支持子类型,那么明确地这样做是个好主意,即使这不是绝对必要的。