10

我正在为 Java 8 证书做准备,以下让我有点困惑,也许有人可以帮我解决这个问题?在示例中,对 Squirrel 类进行了建模。它有名字和重量。现在您可以创建一个 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);
    }
}

到目前为止一切顺利.. 但令人费解的部分。在代码示例下方,他们声明您可以使用方法链接将其编写在一行中。也许我误解了,但是当我链接比较thenComparing部分时,我得到了一个编译错误。这与比较的对象类型有关(首先是 String,然后是 int)。

为什么当我放入中间变量而不是链接时它会起作用?是否有可能完全链接?

4

2 回答 2

9

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

在 的 lambda 参数中指定类型comparing()(或使用方法引用),它解决了推断问题,因为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 教程/文档很好地解释了这一点:

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

于 2018-03-24T20:54:45.277 回答
6

是的,这是可能的 -comparing(...)使用方法引用而不是 lambda 表达式进行thenComparing(...)链接:compare(...)

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

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

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

list.sort(Comparator.comparing(Squirrel::getSpecies).thenComparing(Squirrel::getWeight));
于 2018-03-24T20:53:26.393 回答