Java 8中Comparator的方法链是如何工作的?

10

我正在为Java 8证书做准备,以下内容让我有些困惑,希望有人可以帮助我。 在示例中,建立了一个松鼠类。它有一个名称和一个重量。现在您可以创建一个Comparator类来使用这两个字段对此进行排序。所以首先按名称排序,然后按重量排序。像这样:

public class ChainingComparator implements Comparator<Squirrel> {
    public int compare(Squirrel s1, Squirrel s2) {

        Comparator<Squirrel> c = Comparator.comparing(s -> s.getSpecies());
        c = c.thenComparingInt(s -> s.getWeight());

        return c.compare(s1, s2);
    }
}

到目前为止一切都很好...但是接下来是令人困惑的部分。在代码示例下面,他们声明可以使用方法链接将此写成一个单独的行。也许我误解了,但是当我链接comparingthenComparing部分时,我会得到编译错误。这与比较的对象类型有关(首先是String,然后是int)。

为什么当我放一个中间变量而不是链接时它能工作呢?是否可以进行链接?


4
请尝试这个:Comparator<Squirrel> c = Comparator.comparing((Squirrel s) -> s.getSpecies()).thenComparingInt(s -> s.getWeight()); (该代码为Java语言,用于比较松鼠对象的物种和重量属性,并按照物种进行首要排序,按照重量进行次要排序) - SEY_91
2个回答

9
当你链接两个比较器时,编译器无法推断出comparing()返回的比较器类型参数,因为它依赖于thenComparingInt()返回的比较器本身无法被推断出。
comparing()的lambda参数中指定类型(或使用方法引用),这样就可以解决推断问题,因为comparing()的返回类型也可以被推断出。
    Comparator<Squirrel> c = Comparator.comparing((Squirrel s)  -> s.getSpecies())
                                       .thenComparingInt(s -> s.getWeight());

请注意,在thenComparingInt()的lambda参数中指定类型(或使用方法引用),例如:
    Comparator<Squirrel> c = Comparator.comparing(s -> s.getSpecies())
                                       .thenComparingInt((Squirrel s) -> s.getWeight());

由于接收者(即链接方法的返回类型)在推断类型计算中未被考虑,因此不起作用。

这个JDK 8教程/文档很好地解释了这一点:

注意:重要的是要注意,推断算法仅使用调用参数、目标类型和可能的预期返回类型来推断类型。推断算法不使用程序后面的结果。


6

是的,这是可能的 - 使用方法引用而不是lambda表达式来链式比较comparing(...)thenComparing(...)以及compare(...)

public int compare(Squirrel s1, Squirrel s2) {
    return Comparator.comparing(Squirrel::getSpecies)
        .thenComparing(Squirrel::getWeight)
        .compare(s1, s2);
}

为什么它会这样工作?我无法比Brian在他对类似问题的回答中更好地解释它。

此外,如果您有一个要排序的松鼠列表,这也可以用一行代码重写:

list.sort(Comparator.comparing(Squirrel::getSpecies).thenComparing(Squirrel::getWeight));

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