为什么java.util.Comparator.naturalOrder接受<T extends Comparable<? super T>>并返回Comparator<T>?

9

(如果这是重复的,请指向正确的答案!我搜索并阅读了几个(>5)相关问题,但没有一个看起来符合要求。还查看了Generics FAQ和其他来源...)

显然,当集合类采用比较器时,它应该具有类型Comparator<? super T>,用于参数化类型T。你可以在很多地方看到这一点,例如TreeMap。好的。

我的问题是如何处理Comparator.naturalOrder(),它是基于T extends Comparable<? super T>进行参数化的,但返回一个Comparator<T>。我正在尝试在我的集合类中拥有一个字段,该字段保存用户指定的比较器或Comparator.naturalOrder比较器。

我无法让它工作。我的问题都与以下内容相关:

  1. Comparator.naturalOrder如何正确使用?
    • 我能做我想做的事情吗?即拥有一个字段,在其中存储用户提供的比较器或naturalOrder比较器?
  2. 鉴于大多数集合类(在框架中)是基于T而不是T implements Comparable<? super T>进行参数化的,因此选择了这种设计模式,那么naturalOrder如何有用,因为它需要后者有界通配符,而不是无约束类型参数?

谢谢!

以下是实际示例和编译器错误:

所以:如果我在某个类中有像这样的代码,其中T没有边界(如所有现有的集合类):

class Foo<T> {

    private Comparator<? super T> comparator;

    public void someMethod(Comparator<? super T> comparator)
    {
        this.comparator = comparator;                  // no compile error
        this.comparator = Comparator.naturalOrder();   // incompatible types
    }
}

出现以下错误:

Error:(331, 50) java: incompatible types: inferred type does not conform to upper bound(s)
    inferred: T
    upper bound(s): java.lang.Comparable<? super T>

如果我决定放弃使用? super T的优势,则会有以下结果:

class Foo<T> {

    private Comparator<T> comparator;

    public void someMethod(ComparatorT> comparator)
    {
        this.comparator = comparator;                  // no compile error
        this.comparator = Comparator.naturalOrder();   // incompatible types
    }
}

我需要

Error:(nnn, 50) java: incompatible types: inference variable T has incompatible bounds
    equality constraints: T
    upper bounds: java.lang.Comparable<? super T>
2个回答

11

这个可以编译:

import java.util.*;

class Foo<T extends Comparable<? super T>> {

    private Comparator<T> comparator;

    public void someMethod(Comparator<T> comparator)
    {
       this.comparator = comparator;                  // no compile error
       this.comparator = Comparator.<T>naturalOrder(); // <T> is optional, compiler can infer
    }
}

最简单的想法是:您正试图使用实现了Comparable接口的类型T与Comparator接口,这会对其施加某些要求(特别是它具有T必须实现Comparable接口的复杂递归要求)。在泛型化类时,您并不强制执行此类要求,因此编译器不满意。您对T的要求必须与您正在使用它的类一样强。
您对自然排序方法的理解存在困惑。它只需要一个实现了Comparable接口的类,并为其创建默认的Comparator。没有其他办法--您无法为不可比较的内容创建Comparator。
您希望TreeMap需要Comparable,但您不能这样做,因为使用未实现Comparable接口的内容是有效的,只要您提供了Comparator即可。因此,TreeMap最终不强制执行Comparable,并在运行时显式转换(并抛出异常)。

好的。我并没有真正理解那个特化泛型方法的语法(使用C++术语可能不太合适)。 - davidbak
但我看到你把类型参数的范围限定在了这个类上。如果可能的话,我想要避免这种情况,因为 a) 其他集合框架不是这样工作的, b) 因为你可能有一个类型根本没有以任何方式扩展可比较性,例如 javafx.util.Pair,并且你只想传递一个合适的比较器。 - davidbak
你对自然排序方法的作用感到困惑。它只需要一个实现了Comparable接口的类,并为其创建默认的比较器。没有其他方法可以绕过这一点 - 你不能为不可比较的对象创建比较器。(还要注意,<T>是可选的,编译器可以从左值的类型推断出来,但你确实需要我添加的限制!) - MK.
那就是我的疑惑了。查看那些只用 T 声明的集合(比如 TreeMap)的源代码时,我发现它们在运行时会进行 Comparable 强制转换以获得比较器。感谢您的解释! - davidbak
哈,我没意识到这个。这太搞笑了对吧?你想让TreeMap要求可比较性,但实际上不行,因为使用非可比较的东西是有效的,只要你提供了一个比较器。有时候鸭子类型似乎是个好主意。 - MK.
显示剩余2条评论

2
我猜你必须在你的类Foo中实现Comparable接口才能使用Comparator.naturalOrder()。你必须有一个compareTo(Object o)方法,这个方法实现了自然排序,所以你不需要将其存储在变量中。
我认为在Java 8中,即使在没有实现Comparable接口的类中也可以使用比较器,所以它们不需要实现compareTo(Object o),但是你必须实现这个方法。
@FunctionalInterface
public interface Comparator<T>

这是来自Java 8 API的内容

比较函数,对一些对象集合进行全序排序。可以将比较器传递给排序方法(例如Collections.sort或Arrays.sort),以允许精确控制排序顺序。比较器还可用于控制某些数据结构的顺序(如已排序的集合或已排序的映射表),或为没有自然排序的对象集合提供排序。

一种实现和初始化的方法:

private  Comparator<Operario> ComparatorOperario =
    (o, p)-> o.getNombre().compareTo(p.getNombre());

那么您可以为此变量编写getter和setter,以便更改排序方式。

请注意,类Operario没有实现Comparable接口,而是使用一个比较器Comparator<Operario>来比较Operario类的两个属性,本例中是两个字符串。


我不理解@FunctionalInterface这部分,它有什么区别呢? - MK.
我使用这种接口作为存储lambda的一种方式。例如: private Predicate<LocalDateTime> esActivo = fecha -> ListaJornadas.stream().anyMatch(j->j.caeEnJornada((fecha))); 并且使用它时,esActivo.test(fecha) - Ismael_Diaz
对于@FunctionalInterface注释,它是一种信息性注释,用于定义函数接口。您不必在实现比较器接口时使用它,因为它像Predicate、Function、Bifunction、Consumer一样在Java 8 API中已经定义了。如果您需要定义新的函数接口并且API中没有相应的接口,那么可以使用它。 - Ismael_Diaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接