Verse()不使用 lambda 进行编译

我有一个包含一些 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
33811 次浏览

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

在第二和第三行中,目标类型由于调用 reversed()而中断。我不完全确定原因; reversed()的接收方和返回类型都是 Comparator<T>,因此目标类型似乎应该传播回接收方,但实际上并非如此。(就像我说的,这是一个弱点。)

在第二行中,方法引用提供了填补此空白的其他类型信息。这个信息在第三行中没有出现,因此编译器将 u推断为 Object(最后一招的推断回退) ,这将导致失败。

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

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

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

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

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

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

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

与人们普遍接受的、赞成的答案相反,这与 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());

但是,正如目前公认的答案所示,任何帮助编译器推断 comparing方法调用的 User类型而不采取额外步骤的方法都将有效,因此在这种情况下,您还可以显式地指定 lambda 参数的类型,或者使用同样包含 User类型的方法引用 User::getName