Java Comparator.reversed()不使用lambda编译

Java Comparator.reversed()不使用lambda编译,java,lambda,java-8,comparator,method-reference,Java,Lambda,Java 8,Comparator,Method Reference,我有一个包含一些用户对象的列表,我正在尝试对该列表进行排序,但只能使用方法引用,使用lambda表达式编译器会给出一个错误: List<User> userList = Arrays.asList(u1, u2, u3); userList.sort(Comparator.comparing(u -> u.getName())); // works userList.sort(Comparator.comparing(User::getName).reversed()); //

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

这是编译器类型推断机制中的一个弱点。为了推断lambda中
u
的类型,需要建立lambda的目标类型。这是通过以下方式实现的
userList.sort()
需要类型为
Comparator
的参数。在第一行中,
Comparator.comparating()
需要返回
Comparator
。这意味着
Comparator.comparating()
需要一个
函数
,该函数接受一个
用户
参数。因此,在第一行的lambda中,
u
必须是
User
类型,并且一切正常

在第二行和第三行中,对
reversed()
的调用中断了目标键入。我不完全清楚为什么;接收器和
reversed()
的返回类型都是
比较器
,因此似乎应该将目标类型传播回接收器,但实际上并非如此。(就像我说的,这是一个弱点。)

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

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

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

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

您可以通过使用两个参数
比较器来解决此限制。将
比较器进行比较。reverseOrder()
作为第二个参数:

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

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


Collections.reverseOrder(Comparator)
对类型
T
没有限制。与赏金授予的已被接受且经过投票的答案相反,这与lambdas没有任何关系

汇编如下:

Comparator<LocalDate> dateComparator = naturalOrder();
Comparator<LocalDate> reverseComparator = dateComparator.reversed();
在问题中给出的示例中,这将成为:

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

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

lambda分为隐式类型(参数没有清单类型)和显式类型;方法引用分为精确(无重载)和不精确。当接收器位置中的泛型方法调用具有lambda参数,并且无法从其他参数完全推断出类型参数时,需要提供显式lambda、精确方法ref、目标类型强制转换、,或泛型方法调用的显式类型见证,以提供继续操作所需的其他类型信息。@StuartMarks,您“不完全确定为什么”编译器会这样做。但是语言规范说了什么?是否有足够的信息根据语言规范确定泛型类型?如果是这样,这是一个编译器错误,应该归档并相应处理。否则,这是Java语言应该改进的领域。是哪一个?我认为我们可以将Brian的评论视为确定的,因为他编写了有问题的规范:-)遗憾的是,所有这些都不能解释为什么它在没有反转的情况下工作,而在没有反转的情况下工作。我100%确定这是IDE的问题,因为它没有.reversed()但没有.Nice。与使用显式类型的lambda相比,我更喜欢这个。或者更好的是,
users.sort(reverseOrder(比较(User::getName))
。注意上面的
反向排序(Comparator)
方法在
java.util.Collections
中,而不是在
Comparator
中。注意
Comparator.comparating
有两个类型参数。例如,假设
getName
的类型是
String
,正确的调用应该是:
userList.sort(Comparator.comparing(u->u.getName()).reversed())@snappieT谢谢,修改了答案。
Comparator<LocalDate> reverseComparator = naturalOrder().reversed();
Comparator<LocalDate> reverseComparator = Comparator.<LocalDate>naturalOrder().reversed();
userList.sort(Comparator.comparing<User, String>(u -> u.getName()).reversed());