Comparator.comparing(...) 在获取 String::compareTo 时抛出非静态引用异常

Comparator.comparing(...) throwing non-static reference exception while taking String::compareTo

下面是我的代码片段的两行:

List<String> listDevs = Arrays.asList("alvin", "Alchemist", "brutus", "larsen", "jason", "Kevin");

listDevs.sort(Comparator.comparing(String::length)); //This works fine
listDevs.sort(String::compareToIgnoreCase); //This works fine

但是(出于经验)当我尝试写

listDevs.sort(Comparator.comparing(String::compareToIgnoreCase));

编译器抛出错误

Cannot make a static reference to the non-static method compareToIgnoreCase(String) from the type String

类似的情况发生在下面的代码中

listDevs.sort(Comparator.comparing(String::compareTo));

我理解这个错误,如果我删除 Comparator.comparing(如上所示),它会正常工作。

但我的意思是,这条线是如何工作的?

listDevs.sort(Comparator.comparing(String::length));

我想我错过了什么。我已阅读 this 线程。这是同一个场景吗?

Comparator.comparing 期望 Function 描述可比较的 属性 元素。所以 String::length 就足够了,因为 length()String 的 属性,将 String 评估为 int(这就是为什么 comparingInt在这里更可取)。

相比之下,String.compareToIgnoreCaseString.compareTo比较方法。他们比较两个 String 对象。因此,在需要 Comparator 的地方引用它们就足够了,但在需要 属性 Function 的地方就不够了。

这就像你有一家工厂说“给我一个引擎,我们为你造一辆汽车”,而你正试图给他们一辆完整的汽车。虽然现有的汽车在需要汽车的地方有效,但将其交给工厂制造汽车是没有意义的。

不幸的是,当前的编译器实现在使用函数签名报告错误方面非常糟糕。当签名不匹配时,您几乎总是会看到类似“无法对非静态方法进行静态引用……”的消息。

JLS 表示 ReferenceType :: [TypeArguments] Identifier 的方法引用的编译时声明可以用不同的方式解释。

Given a targeted function type with n parameters, a set of potentially applicable methods is identified:

ReferenceType :: [TypeArguments] Identifier has two different arities, n and n-1, are considered, to account for the possibility that this form refers to either a static method or an instance method.

A method reference expression of the form ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways. If Identifier refers to an instance method, then the implicit lambda expression has an extra parameter with type of this compared to if Identifier refers to a static method. It is possible for ReferenceType to have both kinds of applicable methods, so the search algorithm described above identifies them separately, since there are different parameter types for each case.

Comparator.comparing method accept a Function<T,R extends Comparable<? super R>>。当你使用 String::compareToIgnoreCase 时会报错,因为它有两个参数,一个是隐式的 this 另一个是方法参数的 comparing string , 所以它更像是 BiFunction<String,String,Integer> 而不是 Function<String,Integer>.

BiFunction<String, String, Integer> comparator = String::compareToIgnoreCase;
// you can't assign a BiFunction to a Function
// because one is incompatiable with another.
Function<String,Integer> function = comparator;

Stream.sort method accept a Comparator,而 Comparator 更像是一个 BiFunction<T,T,Integer>,因此它与 String::compareToIgnoreCase 兼容。另一方面,它们可以互换。例如:

Comparator<String> primary = String::compareToIgnoreCase;
BiFunction<String, String, Integer> comparator1 = primary::compare;
Comparator<String> comparator2 = comparator1::apply;

你可以用comparing(String::toLowerCase)代替,它等同于String::compareToIgnoreCase,例如:

// String::compareToIgnoreCase
listDevs.sort(String::compareToIgnoreCase); 

// comparing(String::toLowerCase)
listDevs.sort(comparing(String::toLowerCase))

sort 方法需要 Comparator

当您这样做时,您确实提供了一个。

 listDevs.sort(Comparator.comparing(String::length));

同样发生在这里(但有点不直观):

 listDevs.sort(String::compareToIgnoreCase)
 listDevs.sort((left, right) -> left.compareToIgnoreCase(right)); // same thing as above

这正是 Comparator 的定义 - 采用两个字符串和 return 一个整数。

你说的这行是如何工作的:listDevs.sort(Comparator.comparing(String::length)); 其实很简单。

Comparator.comparing 采用 Function 将您的输入类型转换为 Comparable。在你的情况下需要 String 和 returns 一个 Integer;这是可比的。