MultiSet.Entry和Generics的比较器

时间:2017-10-21 13:21:13

标签: java generics type-inference

我需要一个guava MultiSet.Entry的比较器,用于按计数排序和字符串秒排序。但是,我遇到编译器问题,我认为我得到了泛型错误的东西。

编译:

Comparator<Multiset.Entry<String>> comparator() {
    return Comparator.comparing(Multiset.Entry::getCount);
}

然而,这不是:

Comparator<Multiset.Entry<String>> comparator() {
    return Comparator.comparing(Multiset.Entry::getCount).thenComparing(Multiset.Entry::getElement);
}
Error: java: incompatible types: cannot infer type-variable(s) T,U
    (argument mismatch; invalid method reference
      method getCount in interface com.google.common.collect.Multiset.Entry cannot be applied to given types
        required: no arguments
        found: java.lang.Object
        reason: actual and formal argument lists differ in length)

如果我将比较器组合在一起,例如对于一个字符串,我显然没有类似的问题。

Comparator<String> comparator2() {
    return Comparator.comparing(String::length).thenComparing(String::toString);
}

我在这里缺少什么,我怎样才能让它发挥作用?

(注意:我知道Guava可能有不同的方法,但我想了解我在这里遇到的问题。)

1 个答案:

答案 0 :(得分:2)

我认为是因为类型定位(一种Java推理形式)与代码中使用的点功能编程一起工作(您可以阅读有关目标类型here的内容java文档,特别是名为Target Types and Method Arguments)的部分。

非常简短,如果我有这样的话:

List<String> list = new List<>();

此处的目标类型为List<String>,因此Java(8)意识到您正在创建的新列表已分配给List<String>类型,因此可以推断出类型,并且您可以不必指定类型参数。

编译的第一个代码是:代码Comparator.comparing(Multiset.Entry::getCount);的部分返回Comparator<T>的类型,此结果立即分配给方法的返回类型(即方法的返回类型是目标类型)。编译器可以推断类型,因为目标类型是方法comparator的返回类型,您在方法签名中明确定义/捕获为Comparator<Multiset.Entry<String>>。因此可以推断出T中的Comparator<T>

在第二段代码中(不编译并需要显式类型见证参数 - 方法调用的&lt;&gt;中的显式位),您使用的是连接方法的点运算符。这意味着第二部分thenComparing(Multiset.Entry::getElement)将应用于第一部分Comparator.comparing(Multiset.Entry::getCount)的结果。

第二部分可以从目标类型推断出类型,因为第二部分的结果是方法返回的结果,所以第二部分的目标类型类似于上面编译的情况 - 方法返回类型被捕获并定义,因此它可以推断出这一点。

但是对于第一部分,目标类型是不确定的,因为它没有被分配给具有实际类型的某种类型。根据Comparator API,Comparator<T>是该函数返回的内容,但由于它不知道它将特定分配给哪种类型,因此您需要提供类型见证并明确指定它何时返回Comparator<T> T应该是(在您的情况下)Multiset.Entry<String>