尝试打印JAVA8收集器的结果时出现模糊错误

问题描述 投票:2回答:3

我在尝试打印JAVA8收藏家的结果时遇到Ambiguity错误。

我试图在Product对象中打印ID的总和结果,但得到以下错误:

“方法println(double)对于PrintStream类型是不明确的”

这是一小段代码,我收到编译错误:

编辑:添加代码段以获取更多详细信息:

  1. Product.java域类。

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;
    }

}
  1. 我在编译错误的Main.java类:

以下是我遇到编译错误的代码行:

System.out.println(productList.stream().collect(Collectors.summingDouble(x -> x.getId())));

班级快照:

enter image description here

如果我在一个单独的行中使用Collector(在println方法之外),我不会收到任何错误。

如果我们在qazxsw poi方法中使用它,为什么编译器不能检测到JAVA 8收集器的确切返回类型?

使用命令提示符添加另一种方法的细节:

我尝试使用相同JDK版本的命令提示符,并且程序已成功编译和执行。所以Holger的答案似乎是正确的。这似乎仅适用于Eclipse编译器:

println()

java java-8 java-stream collectors ecj
3个回答
6
投票

这是Eclipse编译器中的一个错误,兔子洞比编译器错误更深。我将你的代码示例缩减为

enter image description here

我只保留了影响编译器行为的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。可能还有其他版本受到影响。


2
投票

是的,这是一个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中的一些背景知识。


0
投票
char[]

我不完全确定这里的确切代码,但没有必需的类型(System.out.println(productsList.stream().mapToDouble(x -> x.id).sum()); 有很多重载参数),并且流的泛型类型,出现歧义。特别是用println而不是Double id。也许其他人可以做更好的解释。

对局部变量的赋值可能有效。

更好的是使用原始类型的流。以上使用double。对于“id”,我宁愿期待一个LongStream。

© www.soinside.com 2019 - 2024. All rights reserved.