Collections.sort()声明:为什么<?超级T>而不是<T>

prv*_*rvn 24 java generics collections comparable

为什么要Collections.sort(List<T>)签名:

public static <T extends Comparable<? super T>> void sort(List<T> list) 
Run Code Online (Sandbox Code Playgroud)

并不是 :

public static <T extends Comparable<T>> void sort(List<? extends T> list)
Run Code Online (Sandbox Code Playgroud)
  • 我知道它们都有同样的目的; 那么为什么框架开发人员使用第一个选项呢?
  • 或者这些声明真的不同吗?

Tag*_*eev 19

您提出的签名可能适用于Java-8.但是在以前的Java版本中,类型推断并不那么聪明.考虑一下你有List<java.sql.Date>.请注意,实现的java.sql.Date扩展.编译时java.util.DateComparable<java.util.Date>

List<java.sql.Date> list = new ArrayList<>();
Collections.sort(list);
Run Code Online (Sandbox Code Playgroud)

它完全适用于Java-7.这里T推断出java.sql.Date实际上是Comparable<java.util.Date>哪个Comparable<? super java.sql.Date>.但是,让我们试试你的签名:

public static <T extends Comparable<T>> void sort(List<? extends T> list) {}

List<java.sql.Date> list = new ArrayList<>();
sort(list);
Run Code Online (Sandbox Code Playgroud)

这里T应该推断为java.util.Date.但是,Java 7规范不允许这样的推断.因此,此代码可以使用Java-8编译,但在Java-7下编译时会失败:

Main.java:14: error: method sort in class Main cannot be applied to given types;
        sort(list);
        ^
  required: List<? extends T>
  found: List<Date>
  reason: inferred type does not conform to declared bound(s)
    inferred: Date
    bound(s): Comparable<Date>
  where T is a type-variable:
    T extends Comparable<T> declared in method <T>sort(List<? extends T>)
1 error
Run Code Online (Sandbox Code Playgroud)

Java-8中的类型推断得到了极大的改进.独立JLS 第18章现在是专门给它,而在Java-7规则简单得多.

  • 还有一点.当你有一个像`List <?的通配符类型?由于通配符类型的工作原理,扩展T> list`你不能在方法内部执行`list.set(index,list.get(anotherIndex))`.这可以通过捕获`的内部辅助方法来规避.将T`扩展为另一个类型变量(或者通过内部实现经常使用未经检查的操作),但是,对于要修改的列表,没有通配符的类型更加清晰. (7认同)
  • @newacct:如果两个变体对调用者没有任何影响,您可能会选择允许更容易实现的变体.尽管如此,开发人员习惯于最初认为`List <?的参数.extends ...>`type不会因为*PECS*规则而被修改,如果你将它用于`sort`方法,则需要再次查看该方法.这不仅仅是一个实施问题. (2认同)

Stu*_*rks 9

// 0
public static <T extends Comparable<? super T>> void sort0(List<T> list) 

// 1
public static <T extends Comparable<T>> void sort1(List<? extends T> list)
Run Code Online (Sandbox Code Playgroud)

这些签名不同,因为它们会对类型之间的关系有不同的要求T和类型参数Comparable的定义T.

假设你有这个类:

class A implements Comparable<Object> { ... }
Run Code Online (Sandbox Code Playgroud)

然后,如果你有

List<A> list = ... ;
sort0(list); // works
sort1(list); // fails
Run Code Online (Sandbox Code Playgroud)

sort1失败的原因是没有类型T可以与自身相比,也就是列表类型的超类型.

事实证明,类A是畸形的,因为Comparable需要满足某些要求的对象.特别是逆转比较应该反转结果的符号.我们可以的实例进行比较的A一个Object,但不是相反,所以这个要求被违反.但是请注意,这是一个需要语义Comparable,而不是由类型系统强加的.仅考虑类型系统,这两个sort声明确实不同.