Comparator.reversed() 不使用 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

答案 1

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

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

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

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

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

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


答案 2

您可以通过将 two 参数 with 用作第二个参数来解决此限制:Comparator.comparingComparator.reverseOrder()

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

推荐