【问题标题】:Java - Definition of the comparing method of Comparator classJava - Comparator 类的比较方法的定义
【发布时间】:2021-04-13 06:17:07
【问题描述】:

想了解Comparator类的方法comparing的定义。我会留下一些代码,以防它有助于解释。我正在使用一个名为 Person 的类,它基本上存储了一个名字和一个姓氏。可以使用 get 方法检索此数据。

public class Person {

    private final String name;
    private final String lastname;

    public Person(String name, String lastname) {
        this.name = name;
        this.lastname = lastname;
    }

    public String getName() { return name; }

    //...
}

然后我创建了一个人员列表:

List<Person> list = Arrays.asList(
    new Person("Juan", "García"),
    new Person("Ana", "Martínez"),
    ...
);

我一直在测试对此人员列表进行排序的不同方法。在其他可能性中,我发现了这个:

list.sort(Comparator.comparing(Person::getName));

我了解这些行的作用。基本上,这个列表是使用比较器排序的,比较器使用排序键(人名)进行比较。此类键是使用对属于 Person 类的 getName 方法的引用来提取的。

不过,我也想了解幕后发生的事情。我的问题是comparing 方法。 Java 文档定义了这样的方法:

static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)

特别是,我正在为泛型类型定义而苦苦挣扎:&lt;T,U extends Comparable&lt;? super U&gt;&gt;;以及参数类型的定义:Function&lt;? super T,? extends U&gt;

T 表示比较器类型,而U 表示键类型,对吧?那么...为什么U 需要扩展Comparable&lt;? super U&gt;,而后者又使用U 的任何超类?

在参数中,该函数使用属于T 的任何超类的对象并返回属于U (&lt;? super T,? extends U&gt;) 的任何子类的对象,但是......为什么?

我希望我的疑问是明确的。另外,对于冗长的解释感到抱歉。

【问题讨论】:

  • you 认为方法的签名应该是什么样的?知道这一点可能会让我清楚你的困惑在哪里。
  • @Sweeper 我不太擅长泛型,还在学习中。无论如何,我的逻辑是这样的:静态 super T, U> Comparator comparison(Function super T, ? extends U> keyExtractor) 它返回一个 T 类型的 Comparator,但它也可以是一个子类,对吧?但是,我想我有点理解函数 (Function super T, ? extends U> keyExtractor) 的定义。但我不确定。

标签: java generics methods lambda comparator


【解决方案1】:

让我们考虑上述方法comparing的签名 static &lt;T,U extends Comparable&lt;? super U&gt;&gt; Comparator&lt;T&gt; comparing(Function&lt;? super T,? extends U&gt; keyExtractor)

  • T 是被比较对象的类型,在上面提到的情况下它是Person

  • U extends &lt;Comparable&lt;? super U&gt;&gt; 描述了用于实现实际比较的类型——这就是为什么这个类型必须是Comparable——在这种情况下是String

  • Function&lt;? super T,? extends U&gt; keyExtractor - keyExtractor 是一个函数,它将 T (Person) 类的对象“转换”为 U (String) 类的可比较对象 - 在这个类中,它是在类 Person 中定义的名称。

【讨论】:

    【解决方案2】:

    我在这里看到了三个主要的困惑:

    为什么是U extends Comparable&lt;? super U&gt;

    这是对U(键的类型)的约束,指定U 可以是哪些特定类型。它说U 必须具有可比性。有道理,对吧?尖括号中的部分指定“U 可以与哪些事物进行比较?”例如,Comparable&lt;Animal&gt; 表示“堪比动物”。约束说“U 必须与 U 的某些超类型相当”。

    您可能想知道为什么它不只是说“U 必须与U 相当”,即U extends Comparable&lt;U&gt;。这是因为我们试图使我们的方法尽可能灵活,即我们尝试接受尽可能多的不同Us。如果我有一个Dog 可以与Animal 进行比较(实现Comparable&lt;Animal&gt;),那么这条狗也可以与另一个Dog 进行比较是合乎逻辑的。但是,由于Dog实现了Comparable&lt;Animal&gt;而不是Comparable&lt;Dog&gt;,它不满足U extends Comparable&lt;U&gt;,这就是我们必须使用U extends Comparable&lt;? extends U&gt;的原因。

    为什么Function? super T

    假设我有一个密钥提取器,它接受 Object,我返回它的 toString() 输出作为比较的密钥(这是比较不常见的事情,但为了这个例子,请耐心等待:D)

    Function<Object, String> stringExtractor = Object::toString;
    

    现在我想要一个比较动物的比较器。我可以使用这个stringExtractor 作为comparing 的参数吗?我当然可以:

    Comparator<Animal> comparator = Comparator.comparing(stringExtractor);
    

    所有动物都有一个toString 方法,因为它们毕竟是Object 的子类!这就是为什么 key extractor 参数采用带有 T 超类的函数的原因 - 允许我们传递上述内容。如果是Function&lt;T, ? extends U&gt;,我们将无法做到Comparator.comparing(stringExtractor)

    同样,我们希望comparing 能够处理尽可能多的事情。

    为什么Function 返回? extends U

    这实际上是完全没有必要的。除非您像这样明确指定 TU

    Comparator.<Foo, Animal>comparing(Foo::getDog);
    

    Function&lt;? super T, U&gt; 还是Function&lt;? super T, ? extends U&gt; 没有区别。有关详细信息,请参阅this question。这个问题是关于thenComparing,签名为comparing

    【讨论】:

    • 谢谢。有几个疑问: 1. 使用 Dog 和 Animal 类 U extends Comparable&lt;? super U&gt;,会被“翻译”为:Dog extends Comparable&lt;any_super_class_of_Dog&gt;。但它不应该说U implements Comparable&lt;? super U&gt;吗? 2.“如果我有一条狗可以与动物进行比较(implements Comparable&lt;Animal&gt;)...”,这意味着如果我们直接从dog 实例,但是使用 Comparator 的比较方法时不起作用,除非 Dog implements Comparable&lt;? super Dog&gt;,对吧?
    • @DamianGDO 1. 在指定通用约束时,您始终使用extends。不要从“继承”的意义上去想它。 2. 是的,你理解正确。除非您有约束 U extends Comparable&lt;? super U&gt;,否则它将无法工作。
    猜你喜欢
    • 2012-11-13
    • 2021-07-08
    • 1970-01-01
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 2018-08-13
    • 2022-06-10
    • 1970-01-01
    相关资源
    最近更新 更多