Comparator.reversed()不使用lambda进行编译

时间:2014-08-07 00:51:17

标签: java lambda java-8 method-reference

我有一个包含一些User对象的列表,我正在尝试对列表进行排序,但只能使用方法引用,使用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 个答案:

答案 0 :(得分:115)

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

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

在第二行中,方法参考提供了填补这一空白的其他类型信息。第三行中没有此信息,因此编译器会将u推断为Object(最后的推理回退),但失败了。

显然,如果你可以使用方法参考,那么这样做就可以了。有时您不能使用方法引用,例如,如果要传递其他参数,则必须使用lambda表达式。在这种情况下,您将在lambda中提供显式参数类型:

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

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

答案 1 :(得分:75)

您可以使用带有Comparator.comparing的双参数Comparator.reverseOrder()作为第二个参数来解决此限制:

users.sort(comparing(User::getName, reverseOrder()));

答案 2 :(得分:1)

与获得赏金的公认答案相反,这与 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());

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

答案 3 :(得分:0)

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

Collections.reverseOrder(Comparator<T>) 对类型 T

没有任何限制