非常困惑于Java 8中的Comparator类型推断。

19 浏览
0 Comments

非常困惑于Java 8中的Comparator类型推断。

我一直在研究Collections.sortlist.sort之间的区别,特别是关于使用Comparator静态方法以及lambda表达式中是否需要参数类型。在开始之前,我知道我可以使用方法引用,比如Song::getTitle来解决我的问题,但我在这里要查询的不是我想要修复的问题,而是我想要得到一个答案,即为什么Java编译器会以这种方式处理它。

这是我的发现。假设我们有一个类型为SongArrayList,其中添加了一些歌曲,有3个标准的获取方法:

ArrayList playlist1 = 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)

接下来,我进一步测试了使用TreeSetObjects.compare

int x = Objects.compare(t1, t2, 
    Comparator.comparing((Song p1) -> p1.getTitle())
    .thenComparing(p1 -> p1.getDuration())
    .thenComparing(p1 -> p1.getArtist())
    );
Set set = new TreeSet(
    Comparator.comparing((Song p1) -> p1.getTitle())
    .thenComparing(p1 -> p1.getDuration())
    .thenComparing(p1 -> p1.getArtist())
    );

与上述情况相同,对于TreeSet,没有编译错误,但是对于Objects.compare,最后一个调用thenComparing显示错误。

请问有人可以解释为什么会发生这种情况,以及为什么在仅调用比较方法(没有进一步调用thenComparing)时根本不需要使用(Song p1)

在同样的主题上,当我对TreeSet做如下操作时:

Set set = new TreeSet(
            Comparator.comparing(p1 -> p1.getTitle())
            .thenComparing(p1 -> p1.getDuration())
            .thenComparing(p1 -> p1.getArtist())
            );

即从比较方法调用的第一个lambda参数中移除类型Song,它在调用比较和第一个调用thenComparing时显示语法错误,但对于最后一个调用thenComparing没有错误,几乎与上述情况相反!而对于其他三个例子,即使用Objects.compareList.sortCollections.sort,当我删除第一个Song参数类型时,所有的调用都会显示语法错误。

编辑以包括我在Eclipse Kepler SR2中收到的错误截图,我现在已经发现这些错误是Eclipse特有的,因为当使用命令行上的JDK8 Java编译器进行编译时,它可以正常编译。

Sort errors in Eclipse

0