尝试打印JAVA8收集器的结果时出现歧义错误
我在打印JAVA8收集器的结果时遇到了模糊错误 我试图打印尝试打印JAVA8收集器的结果时出现歧义错误,java,java-8,java-stream,collectors,ecj,Java,Java 8,Java Stream,Collectors,Ecj,我在打印JAVA8收集器的结果时遇到了模糊错误 我试图打印Product对象中ID总和的结果,但出现以下错误: “println(double)方法对于PrintStream类型不明确” 下面是一小行代码,其中我得到了编译错误: 已编辑:添加代码片段以了解更多详细信息: java域类 包com.sample.replicate.bug public class Product { private double id; private String productName;
Product
对象中ID总和的结果,但出现以下错误:
“println(double)方法对于PrintStream类型不明确”
下面是一小行代码,其中我得到了编译错误:
已编辑:添加代码片段以了解更多详细信息:
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())));
类快照:
如果在单独的行中使用收集器(在println
方法之外),则不会出现任何错误
如果我们在println()
方法中使用JAVA 8收集器,为什么编译器不能检测它的确切返回类型
使用命令提示符添加另一种方法的详细信息:
我尝试使用相同JDK版本的命令提示符,程序编译并成功执行。所以霍尔格的答案似乎是正确的。这似乎仅适用于Eclipse编译器:
我不能完全确定这里的确切代码,但是如果没有必需的类型(println
有许多重载参数),并且流的泛型类型,就会产生歧义。尤其是使用Double id
而不是Double
。也许其他人可以做一个更好的解释
对局部变量的赋值可能有效
更好的方法是使用基元类型的流。上面使用了一个
双流
。对于一个“id”,我更希望是一个长流。这是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)"); }
线程“main”java.lang.ClassCastException中的异常:java.lang.Double无法转换为[C]
在Tmp2.main(未知源)
线程“main”java.lang.ClassCastException中的异常:java.lang.Double无法转换为java.lang.String
在Tmp2.main(未知源)
我认为,我们不需要深入研究正式的Java语言规范,就可以认识到这种行为是不合适的
删除两个不适用的方法,println(char[]x)
和println(String x)
,编译器会选择正确的方法,println(Object x)
而不是println(double x)
,但这并不令人印象深刻
作为参考,我使用版本Oxygen.3a Release(4.7.3a),build 20180405-1200进行了测试。可能还有其他版本也受到影响。是的,这是一个问题,但更仔细的调查表明,这可能是由于JLS中的遗漏造成的
更具体地说,如果将中的一句话更改为如下所示,错误将消失:
旧的:
对于多边形类实例创建表达式或多边形方法调用表达式,C包含所有约束公式,当推断多边形表达式的调用类型时,这些约束公式将出现在§18.5.2生成的集合C中
仅提及“限制条件”似乎是不够的
拟议新设:
对于poly类实例创建表达式或poly方法调用表达式,C包含在推断poly表达式的调用类型时减少和合并§18.5.2生成的集合C所产生的所有类型边界和捕获边界
PS:Javac以在内部和外部推理之间实现比JLS中捕获的更多/不同的数据流而闻名,这可能是Javac选择println(对象)的原因
。在某些方面,此实现可能更接近预期的语义,在本问题的示例中,常识与javac是一致的。这就是为什么IMHO应该专注于改进JLS(以及过渡ecj)
EDIT:虽然上面的分析是可编辑的,修复了问题,甚至可能与javac的实际操作相匹配,但它无法解释为什么问题只出现在println(…)
的重载解析下,而不出现在char[]
变量的赋值中
在对这一差异进行更多研究后,制定了一个替代变更,该变更将有效(通过几个间接方式)强制编译器重新计算捕获边界,而不是像上面建议的那样放弃它。此更改与当前JLS一致。此问题的确切因果链超出了本论坛的范围,但请感兴趣的各方阅读上面链接的Eclipse bug的一些背景。对我来说很好,有什么问题有
id
的类型吗?可能有任何关于它的抽象信息吗?无法在muy环境中复制。您能分享编译器的确切输出或IDE的屏幕截图吗?我得到了
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)"); }
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)"); }