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

Vik*_*ant 9 java static-methods comparator java-8 method-reference

以下是我的代码段的两行:

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

但是(当expermient)当我尝试写

listDevs.sort(Comparator.comparing(String::compareToIgnoreCase));
Run Code Online (Sandbox Code Playgroud)

编译器抛出错误

无法从类型String中对非静态方法compareToIgnoreCase(String)进行静态引用

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

listDevs.sort(Comparator.comparing(String::compareTo));
Run Code Online (Sandbox Code Playgroud)

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

但我的观点是,这条线如何运作?

listDevs.sort(Comparator.comparing(字符串::长度));

我相信我错过了一些东西.我已经读过这个帖子了.这是同样的情况吗?

Hol*_*ger 13

Comparator.comparing期望一个Function描述元素的可比属性.因此,评估a 到a的属性String::length就足够了(这就是为什么这里更好).length()StringStringintcomparingInt

相比之下,String.compareToIgnoreCaseString.compareTo比较的方法.他们比较两个String对象.因此,在Comparator预期的情况下,对它们的引用是足够的,但不是Function预期的属性.

这就像你有一个工厂说"Gimme是一个发动机,我们为你制造一辆汽车",你正试图给他们一辆完整的汽车.虽然现有的汽车在预计有车的情况下是有效的,但将其传递到工厂制造汽车是没有意义的.

不幸的是,当前的编译器实现在报告功能签名错误方面非常糟糕.当签名不匹配时,您几乎总会看到诸如"无法对非静态方法进行静态引用..."之类的消息.

  • `length()`没有参数,`compareTo(String)`有一个参数.两者都是实例方法.所以它们并不相似,但完全不同. (3认同)
  • 不,字符串是输入,整数是输出,反之亦然.这是`length()`的情况,其中输入是你调用此方法的字符串,但不是`compareTo(String)`等,你调用字符串并传递*second*字符串作为参数.使用对实例方法的未绑定方法引用时,您必须学习将接收器实例计为函数参数.否则,无参数方法`length()`也不起作用. (2认同)

hol*_*ava 5

JLSReferenceType :: [TypeArguments] 标识符的方法引用的编译时声明可以用不同的方式解释。

给定具有 n 个参数的目标函数类型,确定一组可能适用的方法:

ReferenceType :: [TypeArguments] 标识符有两个不同的参数,n 和 n-1,被考虑,以解释这种形式引用静态方法或实例方法的可能性。

ReferenceType :: [TypeArguments] Identifier 形式的方法引用表达式可以用不同的方式解释。如果 Identifier 引用一个实例方法,那么与Identifier 引用静态方法相比,隐式 lambda 表达式具有一个额外的this类型参数。ReferenceType 可能有两种适用的方法,因此上述搜索算法分别识别它们,因为每种情况都有不同的参数类型。

Comparator.comparing方法接受一个Function<T,R extends Comparable<? 超级R>>。当您使用String::compareToIgnoreCase的是意志报告错误,因为它有两个参数,一个是隐含的另一种是比较字符串的方法参数的,所以它更像是一个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;
Run Code Online (Sandbox Code Playgroud)

Stream.sort方法接受一个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;
Run Code Online (Sandbox Code Playgroud)

您可以comparing(String::toLowerCase)改用,它等同于String::compareToIgnoreCase,例如:

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

// comparing(String::toLowerCase)
listDevs.sort(comparing(String::toLowerCase))
Run Code Online (Sandbox Code Playgroud)


Eug*_*ene 5

sort方法预计a Comparator.

当你这样做时,你确实提供了一个.

 listDevs.sort(Comparator.comparing(String::length));
Run Code Online (Sandbox Code Playgroud)

这里也发生了同样的事情(但有点不直观):

 listDevs.sort(String::compareToIgnoreCase)
 listDevs.sort((left, right) -> left.compareToIgnoreCase(right)); // same thing as above
Run Code Online (Sandbox Code Playgroud)

这正是a的定义Comparator- 取两个字符串并返回一个int.

你说它是如何工作的线:listDevs.sort(Comparator.comparing(String::length));实际上非常简单.

Comparator.comparing采取一种Function方式将您的输入类型转换为Comparable.在你的情况下,取一个String并返回一个Integer; 这是可比的.