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

And*_*rey 101 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
Run Code Online (Sandbox Code Playgroud)

错误:

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
Run Code Online (Sandbox Code Playgroud)

Stu*_*rks 131

这是编译器类型推理机制的一个弱点.为了推断ulambda中的类型,需要建立lambda的目标类型.这完成如下.userList.sort()期待一种类型的论证Comparator<User>.在第一行,Comparator.comparing()需要返回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());
Run Code Online (Sandbox Code Playgroud)

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

  • Lambdas分为_implicitly-typed_(没有参数的清单类型)和_explicitly-typed_; 方法引用分为_exact_(无重载)和_inexact_.当接收器位置中的泛型方法调用具有lambda参数,并且无法从其他参数完全推断出类型参数时,您需要提供显式lambda,精确方法ref,目标类型转换或显式类型见证.泛型方法调用以提供继续执行所需的其他类型信息. (25认同)
  • 我认为我们可以将Brian的评论视为权威,因为他写了相关规范:-) (8认同)
  • @StuartMarks,您“不完全确定为什么”编译器会这样。但是_语言规范_ 说什么?根据语言规范,是否应该有足够的信息来确定泛型类型?如果是这样,这是一个编译器错误,应该相应地归档和处理。否则,这是 Java 语言应该改进的领域。是哪个? (2认同)
  • 可悲的是,这些都没有解释为什么它在没有反转的情况下工作而没有反转。 (2认同)

Mis*_*sha 81

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

users.sort(comparing(User::getName, reverseOrder()));
Run Code Online (Sandbox Code Playgroud)

  • 注意上面的`reverseOrder(Comparator <T>)`方法在`java.util.Collections`中,而不在`Comparator`中. (7认同)
  • 尼斯.我比使用显式类型的lambda更喜欢这个.或者,更好的是,`users.sort(reverseOrder(比较(User :: getName)));`. (2认同)

her*_*man 16

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

编译如下:

Comparator<LocalDate> dateComparator = naturalOrder();
Comparator<LocalDate> reverseComparator = dateComparator.reversed();
Run Code Online (Sandbox Code Playgroud)

而以下情况则不然:

Comparator<LocalDate> reverseComparator = naturalOrder().reversed();
Run Code Online (Sandbox Code Playgroud)

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

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

Comparator<LocalDate> reverseComparator = Comparator.<LocalDate>naturalOrder().reversed();
Run Code Online (Sandbox Code Playgroud)

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

userList.sort(Comparator.comparing<User, String>(u -> u.getName()).reversed());
Run Code Online (Sandbox Code Playgroud)

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