我在尝试打印JAVA8收藏家的结果时遇到Ambiguity错误。
我试图在Product
对象中打印ID的总和结果,但得到以下错误:
“方法println(double)对于PrintStream类型是不明确的”
这是一小段代码,我收到编译错误:
编辑:添加代码段以获取更多详细信息:
package com.sample.reproduce.bugs;
public class Product {
private double id;
private String productName;
public double getId() {
return id;
}
public void setId(double id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
}
以下是我遇到编译错误的代码行:
System.out.println(productList.stream().collect(Collectors.summingDouble(x -> x.getId())));
班级快照:
如果我在一个单独的行中使用Collector(在println
方法之外),我不会收到任何错误。
如果我们在qazxsw poi方法中使用它,为什么编译器不能检测到JAVA 8收集器的确切返回类型?
使用命令提示符添加另一种方法的细节:
我尝试使用相同JDK版本的命令提示符,并且程序已成功编译和执行。所以Holger的答案似乎是正确的。这似乎仅适用于Eclipse编译器:
println()
这是Eclipse编译器中的一个错误,兔子洞比编译器错误更深。我将你的代码示例缩减为
我只保留了影响编译器行为的public static void main(String[] args)
{
println(Stream.of(42).collect(Collectors.summingDouble(d -> d)));
}
public static void println(double x) {}
public static void println(char[] x) {}
public static void println(String x) {}
public static void println(Object x) {}
方法。
有一些方法println
,它是应该被调用的方法,因为它是唯一适用于没有装箱操作的方法,println(Object x)
,它是错误信息中提到的并且在拆箱后适用的方法,以及两种方法println(double)
和println(char[] x)
,根本不适用。
删除println(String x)
方法会使错误消失,这是可以理解的,即使错误不正确,但奇怪的是,删除println(double x)
方法并不能解决错误。
更糟糕的是,删除任何不适用的方法,println(Object x)
或println(char[] x)
,也会删除错误,但会生成调用错误的,不适用的方法的代码:
println(String x)
public static void main(String[] args)
{
println(Stream.of(42).collect(Collectors.summingDouble(d -> d)));
}
public static void println(double x) { System.out.println("println(double)"); }
public static void println(char[] x) { System.out.println("println(char[])"); }
//public static void println(String x) { System.out.println("println(String)"); }
public static void println(Object x) { System.out.println("println(Object)"); }
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to [C
at Tmp2.main(Unknown Source)
public static void main(String[] args)
{
println(Stream.of(42).collect(Collectors.summingDouble(d -> d)));
}
public static void println(double x) { System.out.println("println(double)"); }
//public static void println(char[] x) { System.out.println("println(char[])"); }
public static void println(String x) { System.out.println("println(String)"); }
public static void println(Object x) { System.out.println("println(Object)"); }
我认为,我们不需要深入了解正式的Java语言规范,将这种行为视为不合适。
删除不适用的方法,Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
at Tmp2.main(Unknown Source)
和println(char[] x)
,使编译器选择正确的方法,println(String x)
超过println(Object x)
,但这并不令人印象深刻。
作为参考,我测试了版本Oxygen.3a Release(4.7.3a),build 20180405-1200。可能还有其他版本受到影响。
是的,这是一个println(double x)
,但更密切的调查表明,这可能是由于JLS中的遗漏造成的。
更具体地说,如果compiler bug中的一个句子改变如下,那么bug就会消失:
旧:
对于多类实例创建表达式或多边形方法调用表达式,C包含在推断多重表达式的调用类型时将出现在由第18.5.2节生成的集合C中的所有约束公式。
仅提及“约束论坛”似乎是不够的。
拟议新:
对于多类实例创建表达式或多边形方法调用表达式,C包含在推断多重表达式的调用类型时减少和合并由§18.5.2生成的集合C所产生的所有类型边界和捕获边界。
PS:Javac以在JLS中捕获的内部和外部推理之间实现更多/不同的数据流而闻名,这可能是javac选择JLS §18.5.2.2.的原因。在某些方面,这种实现可能更接近于预期的语义,并且在该问题的示例中,常识与javac一致。这就是为什么IMHO应该专注于改进JLS(和传递ecj)。
编辑:虽然上面的分析是合理的,修复了问题,甚至可能匹配javac实际正在做的事情,它无法解释为什么问题只发生在println(Object)
的重载分辨率之下,而不是在println(..)
变量的赋值中。
在对这种差异进行更多研究之后,制定了另一种变化,这将有效地(通过几个间接)强制编译器重新计算捕获界限,而不是像上面提到的那样传递它。此更改与当前的JLS一致。这个问题的确切因果链超出了本论坛的范围,但是有兴趣的人士可以阅读上面链接的Eclipse bug中的一些背景知识。
char[]
我不完全确定这里的确切代码,但没有必需的类型(System.out.println(productsList.stream().mapToDouble(x -> x.id).sum());
有很多重载参数),并且流的泛型类型,出现歧义。特别是用println
而不是Double id
。也许其他人可以做更好的解释。
对局部变量的赋值可能有效。
更好的是使用原始类型的流。以上使用double
。对于“id”,我宁愿期待一个LongStream。