我一直在研究 Collections.sort
和 list.sort
之间的区别,特别是关于使用 Comparator
静态方法以及 lambda 表达式中是否需要参数类型。在我们开始之前,我知道我可以使用方法引用,例如 Song::getTitle
来克服我的问题,但是我在这里的查询并不是我想要修复的东西,而是我想要一个答案的东西,即为什么 Java 编译器以这种方式处理它。
这是我的发现。假设我们有一个 Song
类型的 ArrayList
,加上一些歌曲,有3个标准的 get 方法:
ArrayList<Song> playlist1 = new ArrayList<Song>();
//add some new Song objects
playlist.addSong( new Song("Only Girl (In The World)", 235, "Rhianna") );
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
,即 List,这解决了以下两个 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())
);
Set<Song> set = new TreeSet<Song>(
Comparator.comparing((Song p1) -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
同样的事情发生在,对于 TreeSet
,没有编译错误,但是对于 Objects.compare
,对 thenComparing
的最后一次调用显示了一个错误。
有没有人能解释一下为什么会发生这种情况,以及为什么在简单地调用比较方法时根本不需要使用 (Song p1)
(没有进一步的 thenComparing
调用)。
关于同一主题的另一个查询是,当我对 TreeSet
执行此操作时:
Set<Song> set = new TreeSet<Song>(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
也就是说,从比较方法调用的第一个 lambda 参数中移除类型 Song
,它显示了比较调用和对 thenComparing
的第一次调用下的语法错误,但是没有显示对 thenComparing
的最后一次调用——几乎与上面发生的情况相反!然而,对于所有其他3个例子,即 Objects.compare
、 List.sort
和 Collections.sort
,当我删除第一个 Song
参数类型时,它显示所有调用的语法错误。
非常感谢。
在 Eclipse Kepler SR2中编辑了错误的截图,现在我发现它是 Eclipse 特有的,因为在命令行上使用 JDK8 Java 编译器编译时,它的编译是 OK 的。