非常困惑于Java 8中的Comparator类型推断。
非常困惑于Java 8中的Comparator类型推断。
我一直在研究Collections.sort
和list.sort
之间的区别,特别是关于使用Comparator
静态方法以及lambda表达式中是否需要参数类型。在开始之前,我知道我可以使用方法引用,比如Song::getTitle
来解决我的问题,但我在这里要查询的不是我想要修复的问题,而是我想要得到一个答案,即为什么Java编译器会以这种方式处理它。
这是我的发现。假设我们有一个类型为Song
的ArrayList
,其中添加了一些歌曲,有3个标准的获取方法:
ArrayListplaylist1 = new ArrayList (); //添加一些新的Song对象 playlist.addSong( new Song("Only Girl (In The World)", 235, "Rihanna") ); playlist.addSong( new Song("Thinking of Me", 206, "Olly Murs") ); playlist.addSong( new Song("Raise Your Glass", 202,"P!nk") );
这是对两种类型的排序方法的调用,没有问题:
Collections.sort(playlist1, Comparator.comparing(p1 -> p1.getTitle())); playlist1.sort( Comparator.comparing(p1 -> p1.getTitle()));
一旦我开始链式调用thenComparing
,就会出现以下情况:
Collections.sort(playlist1, Comparator.comparing(p1 -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) ); playlist1.sort( Comparator.comparing(p1 -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) );
即,语法错误,因为它不再知道p1
的类型。因此,为了修复这个问题,我在第一个参数(比较器)中添加了类型Song
:
Collections.sort(playlist1, Comparator.comparing((Song p1) -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) ); playlist1.sort( Comparator.comparing((Song p1) -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) );
现在来到了令人困惑的部分。对于playlist1.sort
,即列表,它解决了所有的编译错误,对于以下两个thenComparing
调用都是如此。然而,对于Collections.sort
,它解决了第一个调用的错误,但是对于最后一个调用却没有解决。我测试了添加了几个额外的thenComparing
调用,它总是在最后一个调用上显示错误,除非我为参数加上(Song p1)
。
接下来,我进一步测试了使用TreeSet
和Objects.compare
:
int x = Objects.compare(t1, t2, Comparator.comparing((Song p1) -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) ); Setset = new TreeSet ( Comparator.comparing((Song p1) -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) );
与上述情况相同,对于TreeSet
,没有编译错误,但是对于Objects.compare
,最后一个调用thenComparing
显示错误。
请问有人可以解释为什么会发生这种情况,以及为什么在仅调用比较方法(没有进一步调用thenComparing
)时根本不需要使用(Song p1)
。
在同样的主题上,当我对TreeSet
做如下操作时:
Setset = new TreeSet ( Comparator.comparing(p1 -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) );
即从比较方法调用的第一个lambda参数中移除类型Song
,它在调用比较和第一个调用thenComparing
时显示语法错误,但对于最后一个调用thenComparing
没有错误,几乎与上述情况相反!而对于其他三个例子,即使用Objects.compare
、List.sort
和Collections.sort
,当我删除第一个Song
参数类型时,所有的调用都会显示语法错误。
编辑以包括我在Eclipse Kepler SR2中收到的错误截图,我现在已经发现这些错误是Eclipse特有的,因为当使用命令行上的JDK8 Java编译器进行编译时,它可以正常编译。