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

How does method chaining work in Java 8 Comparator?

我正在准备 Java 8 证书,下面的内容让我有点困惑,也许有人可以帮我解决这个问题? 在示例中,模拟了一只松鼠 class。它有一个名字和一个权重。现在你可以创建一个 Comparator class 来使用这两个字段对这个东西进行排序。所以首先按名称排序,然后按重量排序。像这样:

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)。

为什么当我放入一个中间变量而不是链接时它会起作用?是否可以链接?

是的,可以 - 使用方法引用而不是 lambda 表达式将 comparing(...)thenComparing(...)compare(...) 链接起来:

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

为什么会这样?在他的 answer to a similar question.

中,我无法比 Brian 更好地解释它

此外,这可以用 1 行重写(假设您有一个要排序的松鼠列表):

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

当你链接两者时,编译器无法推断 comparing() 的 returned 比较器的类型参数,因为它依赖于 thenComparingInt() 的 returned 比较器这本身是无法推断的。

comparing() 的 lambda 参数中指定类型(或使用方法引用),它解决了推断问题,因为可以推断出 comparing() 的 returned 类型. :

    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());

将不起作用,因为在推理类型计算中不考虑接收器(这里是链接方法的 return 类型)。

This JDK 8 tutorial/documentation 解释得很好 :

Note: It is important to note that the inference algorithm uses only invocation arguments, target types, and possibly an obvious expected return type to infer types. The inference algorithm does not use results from later in the program.