【问题标题】:Java var and inference type ambiguityJava var 和推理类型歧义
【发布时间】:2021-02-19 13:54:40
【问题描述】:

两个调用都是正确的:

Collectors.groupingBy((String s)->s.toLowerCase(),Collectors.counting());
Collectors.groupingBy((String s)->s.toLowerCase(Locale.ENGLISH),Collectors.counting());

从那以后,为什么下面的错误:

Collectors.groupingBy(String::toLowerCase,Collectors.counting());

毕竟String::toLowerCase不能对应第二个……那为什么IntelliJ会说Reference to 'toLowerCase' is ambiguous, both 'toLowerCase(Locale)' and 'toLowerCase()' match呢?

String::toLowerCase 必须明确解析为 (String s)->s.toLowerCase() 还是我错过了什么?

当然,如果我为 IntelliJ 提供更多上下文,例如:

Collector<String,?,Map<String,Long>> c = Collectors.groupingBy(String::toLowerCase,Collectors.counting());

这是正确的,但是在 Java 10 var 推断类型上下文中它是错误的:

var c = Collectors.groupingBy(String::toLowerCase,Collectors.counting());

我了解编译器无法推断counting 的输入类型。如果我写:

Collector<String,?,Long> counter = Collectors.counting();
var c = Collectors.groupingBy(String::toLowerCase,counter);

它是正确的。因此,为什么编译器不能推断出唯一可接受的形式?

-------编辑--------

我交替使用 IntelliJ/compiler 只是因为我首先使用 IntelliJ 并且报告的错误是:

Reference to 'toLowerCase' is ambiguous, both 'toLowerCase(Locale)' and 'toLowerCase()' match

编译器的错误更加难以理解(但包含更多关于推理失败原因的提示),例如:

Demo.java:31: error: incompatible types: cannot infer type-variable(s) T#1,K,A,D,CAP#1,T#2
        Collectors.groupingBy(String::toLowerCase,Collectors.counting());
                             ^
    (argument mismatch; invalid method reference
      incompatible types: Object cannot be converted to Locale)
  where T#1,K,A,D,T#2 are type-variables:
    T#1 extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
    K extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
    A extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
    D extends Object declared in method <T#1,K,A,D>groupingBy(Function<? super T#1,? extends K>,Collector<? super T#1,A,D>)
    T#2 extends Object declared in method <T#2>counting()
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?

【问题讨论】:

  • 在我看来是个错误。
  • “那么为什么 IntelliJ 说”你是说这只发生在 intellij 中,还是它也发生在另一个编译器中?
  • Eclipse 上也一样:编译器似乎将Collectors.counting() 的类型参数推断为Object,因此它期望String 有一个方法toLowerCase(Object)
  • Netbeans 上的 Java 14,此处为 Windows。同样的问题。如果我对方法引用进行类型转换,编译器错误就会消失:(Function&lt;String, String&gt;) String::toLowerCase
  • @AndyTurner 实际上编译器会报告错误,这些错误由 intellij “解释”以使它们更“可读”。

标签: java type-inference java-10


【解决方案1】:

这是编译器的“弱点”,至少在 this JEP is in place 之前是这样。

我已经回答了几乎完全相同的问题here。还有一个answer from JDK core developers too

还有yet another question跟你很亲近。

重要的是已知这有时会导致问题,但有一个简单的解决方案 - 根据JLS,使用lambda,因此使用显式类型。

【讨论】:

  • 这里不需要 lambda,因为您通常不会将 Collectors.groupingBy 作为语句调用,也不会将其分配给变量。只需在Stream 上将其用作collect 的参数,就没有问题……
【解决方案2】:

我的猜测是编译器在String 类中发现了两次出现的toLowerCase,因此它决定首先从第二个参数Collectors.counting() 进行推断,该参数被解析为Object。这会导致编译器抛出错误,因为它找不到任何接受 ObjecttoLowerCase() 方法。

如果我们尝试定义一个方法来使用它作为替代:

static String toLowerCase(String s) {
    return s.toLowerCase();
}

然后以下将起作用:

Collectors.groupingBy(Test::toLowerCase, Collectors.counting()); // compiles ok

但是如果我们再引入一个重载,问题又出现了:

static String toLowerCase(String s) {
    return s.toLowerCase();
}

static String toLowerCase(String s, Locale locale) {
    return s.toLowerCase(locale);
}

Collectors.groupingBy(Test::toLowerCase,Collectors.counting()); // fails again

【讨论】:

  • 很好的参考,但不完全是问题,因为我在这里没有使用任何通用方法参考。但几乎同样的问题......
  • @Jean-BaptisteYunès 实际上你是对的,它并不完全相同,因为在报告的错误中,get() 方法没有重载。但我仍然认为这是由于方法解析重载造成的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-29
相关资源
最近更新 更多