Java 8 Comparator类型推断非常困惑 [英] Very confused by Java 8 Comparator type inference

查看:209
本文介绍了Java 8 Comparator类型推断非常困惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在研究 Collections.sort list.sort 之间的区别,特别是关于使用 Comparator 静态方法以及lambda表达式中是否需要param类型。在我们开始之前,我知道我可以使用方法参考,例如 Song :: getTitle 来克服我的问题,但我的查询并不是我想修复的东西,而是我想要的答案,即为什么Java编译器处理就这样。

I've been looking at the difference between Collections.sort and list.sort, specifically regarding using the Comparator static methods and whether param types are required in the lambda expressions. Before we start, I know I could use method references, e.g. Song::getTitle to overcome my problems, but my query here is not so much something I want to fix but something I want an answer to, i.e. why is the Java compiler handling it in this way.

这是我的发现。假设我们有一个类型 Song ArrayList ,添加了一些歌曲,有3种标准的get方法:

These are my finding. Suppose we have an ArrayList of type Song, with some songs added, there are 3 standard get methods:

    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") );

这是对两种类型的排序方法的调用,没问题:

Here is a call to both types of sort method that works, no problem:

Collections.sort(playlist1, 
            Comparator.comparing(p1 -> p1.getTitle()));

playlist1.sort(
            Comparator.comparing(p1 -> p1.getTitle()));

我一开始连锁然后比较 ,会发生以下情况:

As soon as I start to chain thenComparing, the following happens:

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 添加到第一个参数(比较):

i.e. syntax errors because it does not know the type of p1 anymore. So to fix this I add the type Song to the first parameter (of comparing):

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())
        );

现在出现了CONFUSING部分。对于p laylist1.sort ,即List,这解决了以下 thenComparing 调用的所有编译错误。但是,对于 Collections.sort ,它会解决第一个问题,但不是最后一个问题。我测试添加了几个额外的调用 thenComparing 并且它总是显示最后一个的错误,除非我把(Song p1)用于参数。

Now here comes the CONFUSING part. For playlist1.sort, i.e. the List, this solve all compilation errors, for both the following thenComparing calls. However, for Collections.sort, it solves it for the first one, but not the last one. I tested added several extra calls to thenComparing and it always shows an error for the last one, unless I put (Song p1) for the parameter.

现在我继续通过创建 TreeSet 并使用<进行测试code> Objects.compare :

Now I went on to test this further with creating a TreeSet and with using 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 显示错误。

The same thing happens as in, for the TreeSet, there are no compilation errors but for Objects.compare the last call to thenComparing shows an error.

任何人都可以解释为什么会这样,以及为什么没有必要使用(Song p1)只需调用比较方法(不再进一步然后比较调用)。

Can anyone please explain why this is happening and also why there is no need to use (Song p1) at all when simply calling the comparing method (without further thenComparing calls).

关于同一主题的另一个查询是当我这样做 TreeSet

One other query on the same topic is when I do this to the TreeSet:

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

即。从比较方法调用的第一个lambda参数中删除类型 Song ,它显示在比较调用和第一次调用之后的语法错误然后比较但不是最后一次调用 thenComparing - 几乎与上面发生的相反!然而,对于所有其他3个示例,即 Objects.compare List.sort Collections.sort 当我删除第一个 Song param类型时,它会显示所有调用的语法错误。

i.e. remove the type Song from the first lambda parameter for the comparing method call, it shows syntax errors under the call to comparing and the first call to thenComparing but not to the final call to thenComparing - almost the opposite of what was happening above! Whereas, for all the other 3 examples i.e. with Objects.compare, List.sort and Collections.sort when I remove that first Song param type it shows syntax errors for all the calls.

非常感谢提前。

编辑包含我在Eclipse Kepler SR2中收到的错误的截图,我现在发现这些错误是特定于Eclipse的,因为当在命令行上使用JDK8 java编译器编译它编译好了。

Edited to include screenshot of errors I was receiving in Eclipse Kepler SR2, which I have now since found are Eclipse specific because when compiled using the JDK8 java compiler on the command-line it compiles OK.

推荐答案

首先,你所说的导致错误的所有例子都可以通过参考实现(来自JDK 8的javac)编译得很好。它们在IntelliJ中工作正常,所以很可能你看到的错误是特定于Eclipse的。

First, all the examples you say cause errors compile fine with the reference implementation (javac from JDK 8.) They also work fine in IntelliJ, so its quite possible the errors you're seeing are Eclipse-specific.

您的基本问题似乎是:当我开始链接时,为什么它会停止工作。原因是,虽然lambda表达式和泛型方法调用在它们作为方法参数出现时是多义表达式(它们的类型是上下文相关的),但当它们显示为方法接收器表达式时,它们不是。

Your underlying question seems to be: "why does it stop working when I start chaining." The reason is, while lambda expressions and generic method invocations are poly expressions (their type is context-sensitive) when they appear as method parameters, when they appear instead as method receiver expressions, they are not.

当你说

Collections.sort(playlist1, comparing(p1 -> p1.getTitle()));

有足够的类型信息来解决的类型参数比较( )和参数类型 p1 比较()调用从 Collections.sort 的签名中获取其目标类型,因此它已知为比较()必须返回比较器< Song> ,因此 p1 必须是歌曲

there is enough type information to solve for both the type argument of comparing() and the argument type p1. The comparing() call gets its target type from the signature of Collections.sort, so it is known comparing() must return a Comparator<Song>, and therefore p1 must be Song.

但是当你开始链接时:

Collections.sort(playlist1,
                 comparing(p1 -> p1.getTitle())
                     .thenComparing(p1 -> p1.getDuration())
                     .thenComparing(p1 -> p1.getArtist()));

现在我们遇到了问题。我们知道复合表达式比较(...)。thenComparing(...)的目标类型为 Comparator< Song> ,但因为链的接收器表达式比较(p - > p.getTitle()),是一个通用的方法调用,我们可以'从其他论点推断出它的类型参数,我们有点不走运。由于我们不知道这个表达式的类型,我们不知道它有一个 thenComparing 方法等。

now we've got a problem. We know that the compound expression comparing(...).thenComparing(...) has a target type of Comparator<Song>, but because the receiver expression for the chain, comparing(p -> p.getTitle()), is a generic method call, and we can't infer its type parameters from its other arguments, we're kind of out of luck. Since we don't know the type of this expression, we don't know that it has a thenComparing method, etc.

有几种方法可以解决这个问题,所有这些方法都涉及注入更多类型信息,以便可以正确键入链中的初始对象。在这里,它们按照降低需求和增加侵入性的粗略顺序:

There are several ways to fix this, all of which involve injecting more type information so that the initial object in the chain can be properly typed. Here they are, in rough order of decreasing desirability and increasing intrusiveness:


  • 使用精确的方法引用(没有重载的引用),如宋::的getTitle 。然后,这会提供足够的类型信息来推断比较()调用的类型变量,从而为它提供一个类型,从而继续沿着链继续。

  • 使用显式lambda(如您在示例中所做的那样)。

  • 提供类型见证比较() call: Comparator。< Song,String>比较(...)

  • 通过将接收器表达式转换为 Comparator< Song> ,提供带有强制转换的显式目标类型。

  • Use an exact method reference (one with no overloads), like Song::getTitle. This then gives enough type information to infer the type variables for the comparing() call, and therefore give it a type, and therefore continue down the chain.
  • Use an explicit lambda (as you did in your example).
  • Provide a type witness for the comparing() call: Comparator.<Song, String>comparing(...).
  • Provide an explicit target type with a cast, by casting the receiver expression to Comparator<Song>.

这篇关于Java 8 Comparator类型推断非常困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆