131

我有一个包含一些用户对象的列表,我正在尝试对列表进行排序,但只能使用方法引用,使用 lambda 表达式编译器会给出错误:

List<User> userList = Arrays.asList(u1, u2, u3);
userList.sort(Comparator.comparing(u -> u.getName())); // works
userList.sort(Comparator.comparing(User::getName).reversed()); // works
userList.sort(Comparator.comparing(u -> u.getName()).reversed()); // Compiler error

错误:

com\java8\collectionapi\CollectionTest.java:35: error: cannot find symbol
            userList.sort(Comparator.comparing(u -> u.getName()).reversed());
                                                     ^
symbol:   method getName()
location: variable u of type Object
1 error
4

4 回答 4

180

这是编译器类型推断机制的一个弱点。为了推断lambda 中的类型,需要建立 lambdau目标类型。这是按如下方式完成的。userList.sort()期待一个类型的参数Comparator<User>。在第一行,Comparator.comparing()需要 return Comparator<User>。这意味着Comparator.comparing()需要Function一个User参数。因此,在第一行的 lambda 中,u必须是类型User并且一切正常。

在第二行和第三行中,目标类型因对 的调用而中断reversed()。我不完全确定为什么;接收器和返回类型reversed()都是Comparator<T>,所以看起来目标类型应该传播回接收器,但事实并非如此。(就像我说的,这是一个弱点。)

在第二行中,方法引用提供了填补这一空白的附加类型信息。第三行中没有此信息,因此编译器推断uObject(最后手段的推断回退),但失败了。

显然,如果您可以使用方法引用,那就这样做吧,它会起作用的。有时你不能使用方法引用,例如,如果你想传递一个额外的参数,那么你必须使用一个 lambda 表达式。在这种情况下,您将在 lambda 中提供显式参数类型:

userList.sort(Comparator.comparing((User u) -> u.getName()).reversed());

在未来的版本中,可能会增强编译器以涵盖这种情况。

于 2014-08-07T03:23:37.453 回答
102

您可以通过使用双参数Comparator.comparingwithComparator.reverseOrder()作为第二个参数来解决此限制:

users.sort(comparing(User::getName, reverseOrder()));
于 2014-08-07T21:05:59.947 回答
4

与已获得赏金的已接受和赞成的答案相反,这与 lambdas 没有任何关系。

以下编译:

Comparator<LocalDate> dateComparator = naturalOrder();
Comparator<LocalDate> reverseComparator = dateComparator.reversed();

而以下没有:

Comparator<LocalDate> reverseComparator = naturalOrder().reversed();

这是因为编译器的类型推断机制不够强大,无法一次采取两个步骤:确定reversed()方法调用需要类型参数LocalDate,因此naturalOrder()方法调用也需要相同的类型参数。

有一种方法可以调用方法并显式传递类型参数。在简单的情况下,它不是必需的,因为它是推断出来的,但可以通过以下方式完成:

Comparator<LocalDate> reverseComparator = Comparator.<LocalDate>naturalOrder().reversed();

在问题中给出的示例中,这将变为:

userList.sort(Comparator.comparing<User, String>(u -> u.getName()).reversed());

但如当前接受的答案所示,任何有助于编译器推断方法调用类型而无需采取额外步骤Usercomparing方法都将起作用,因此在这种情况下,您还可以显式指定 lambda 参数的类型或使用User::getName还包括的方法引用类型User

于 2021-03-30T16:08:20.960 回答
0

静态方法Collections.reverseOrder(Comparator<T>)似乎是已经提出的最优雅的解决方案。只有一个警告: Comparator.reverseOrder()要求 T 实现可比较并依赖于自然排序顺序。

Collections.reverseOrder(Comparator<T>)对类型没有限制T

于 2021-02-27T18:34:43.633 回答