【问题标题】:Collections.sort() declaration: why <? super T> rather than <T>Collections.sort() 声明:为什么是 <?超级 T> 而不是 <T>
【发布时间】:2016-03-01 05:12:58
【问题描述】:

为什么Collections.sort(List&lt;T&gt;)有签名:

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

而不是:

public static <T extends Comparable<T>> void sort(List<? extends T> list)
  • 我了解它们的用途相同;那么为什么框架开发者会使用第一个选项呢?
  • 或者这些声明真的不同?

【问题讨论】:

    标签: java generics collections comparable


    【解决方案1】:

    您提议的签名可能适用于 Java-8。然而,在以前的 Java 版本中,类型推断并不是那么聪明。考虑到你有List&lt;java.sql.Date&gt;。请注意,java.sql.Date 扩展了实现 Comparable&lt;java.util.Date&gt;java.util.Date。编译时

    List<java.sql.Date> list = new ArrayList<>();
    Collections.sort(list);
    

    它在 Java-7 中完美运行。这里T 被推断为java.sql.Date,实际上是Comparable&lt;java.util.Date&gt;,即Comparable&lt;? super java.sql.Date&gt;。不过让我们试试你的签名:

    public static <T extends Comparable<T>> void sort(List<? extends T> list) {}
    
    List<java.sql.Date> list = new ArrayList<>();
    sort(list);
    

    这里的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
    

    Java-8 中的类型推断得到了极大的改进。单独的 JLS chapter 18 现在专用于它,而在 Java-7 中规则 were 要简单得多。

    【讨论】:

    • 但是静态 > T max(Collection extends T> c) 工作正常……这又是一个推理问题吗?
    • @prvn,这里你有Comparable&lt;? super T&gt;,而不是Comparable&lt;T&gt;,就像你提议的签名一样。 &lt;T extends Comparable&lt;? super T&gt;&gt; void sort(List&lt;? extends T&gt; list) 也可以,但是在这里添加? extends T 是完全没有必要的,而在max 方法中是合理的(因为max 返回值)。
    • 还有一点。当你有像List&lt;? extends T&gt; list 这样的通配符类型时,由于通配符类型的工作方式,你不能在方法内执行list.set(index, list.get(anotherIndex))。这可以通过将? extends T 捕获到另一个类型变量中的内部辅助方法来规避(或者通过像内部实现那样使用未经检查的操作),但是对于要修改的列表来说,没有通配符的类型仍然更清晰。
    • @Holger:但这是实现者的内部实现问题。我们只关注公共 API,它不应该受到内部实现细节的影响。
    • @newacct:如果两个变体对调用者没有影响,您可能会选择一个更容易实现的变体。尽管如此,开发人员最初习惯于认为 List&lt;? extends …&gt; 类型的参数不会因为 PECS 规则而被修改,如果您将其用于 @,则需要再次查看该方法987654346@ 方法。这不仅仅是一个实施问题。
    【解决方案2】:
    // 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)
    

    这些签名不同,因为它们对T 类型和T 定义中Comparable 的类型参数之间的关系提出了不同的要求。

    假设你有这个类:

    class A implements Comparable<Object> { ... }
    

    如果你有

    List<A> list = ... ;
    sort0(list); // works
    sort1(list); // fails
    

    sort1 失败的原因是 没有 类型 T 既可以与自身进行比较,也可以是列表类型的超类型。

    原来A 类的格式不正确,因为Comparable 的对象需要满足某些要求。特别是反转比较应该反转结果的符号。我们可以将A 的实例与Object 进行比较,但反之则不行,因此违反了此​​要求。但请注意,这是Comparable语义 的要求,不是类型系统强加的。仅考虑类型系统,两个sort 声明确实不同。

    【讨论】:

      【解决方案3】:

      它们不同是因为? super T 的限制性低于T。它是一个Lower Bounded Wildcard(链接的 Java 教程部分表示)

      术语List&lt;Integer&gt;List&lt;? super Integer&gt; 更具限制性,因为前者仅匹配Integer 类型的列表,而后者匹配作为Integer 超类型的任何类型的列表。

      Integer 替换为T 表示T java.lang.Object

      【讨论】:

      • 但我正在增加 sort() 参数的灵活性 ..using ?扩展,
      • 是的,没有人说List&lt;? super Integer&gt;List&lt;Integer&gt; 相同。第二个版本使用extends,问题是这是否相等。
      • 我认为您的回答与问题没有直接关系。
      猜你喜欢
      • 1970-01-01
      • 2011-05-12
      • 2020-09-19
      • 2020-02-10
      • 2019-10-11
      • 1970-01-01
      • 2019-04-14
      • 2010-10-14
      • 1970-01-01
      相关资源
      最近更新 更多