尝试打印 JAVA8 收集器的结果时出现歧义错误
Ambiguity error while trying to print result of JAVA8 Collector
我在尝试打印 JAVA8 个收集器的结果时出现歧义错误。
我正在尝试打印 Product
对象中 ID 的总和结果,但出现以下错误:
"The method println(double) is ambiguous for the type PrintStream"
这是我遇到编译错误的一小行代码:
已编辑:添加代码片段以获取更多详细信息:
- Product.java 域 class.
包 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;
}
}
- Main.java class 我遇到编译错误的地方:
以下是我遇到编译错误的代码行:
System.out.println(productList.stream().collect(Collectors.summingDouble(x -> x.getId())));
Class 快照:
如果我将在单独的行中使用 Collector(在 println
方法之外),我不会收到任何错误。
如果我们在 println()
方法中使用它,为什么编译器无法检测到 JAVA 8 个收集器的确切 return 类型?
使用命令提示符添加另一种方法的详细信息:
我尝试使用相同 JDK 版本的命令提示符,程序编译并成功执行。所以霍尔格的回答似乎是正确的。这似乎只与 Eclipse 编译器有关:
System.out.println(productsList.stream().mapToDouble(x -> x.id).sum());
我不完全确定这里的确切代码,但是没有 必需的 类型(println
有很多重载参数)和流的通用类型,出现歧义。特别是 Double id
而不是 double
。 也许其他人可以做更好的解释。
对局部变量的赋值可能有效。
更好的是使用原始类型的流。上面使用了一个DoubleStream
。对于 "id",我宁愿期待 LongStream。
这是 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)"); }
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
at Tmp2.main(Unknown Source)
我认为,我们不需要深入研究正式的 Java 语言规范,就可以识别这种行为是不合适的。
删除两个不适用的方法,println(char[] x)
和 println(String x)
,使编译器选择正确的方法,println(Object x)
而不是 println(double x)
,但这并不令人印象深刻。
作为参考,我测试了版本 Oxygen.3a Release (4.7.3a),build 20180405-1200。可能还有其他版本受到影响。
是的,这是一个compiler bug,但仔细调查表明这可能是由于 JLS 中的一个遗漏造成的。
更具体地说,如果 JLS §18.5.2.2. 中的一个句子像这样更改,错误就会消失:
旧:
For a poly class instance creation expression or a poly method invocation expression , C contains all the constraint formulas that would appear in the set C generated by §18.5.2 when inferring the poly expression's invocation type.
仅提及"constraint forumulas"似乎还不够。
提议的新:
For a poly class instance creation expression or a poly method invocation expression , C contains all the type bounds and capture bounds that would result from reducing and incorporating the set C generated by §18.5.2 when inferring the poly expression's invocation type.
PS:Javac 以在内部和外部推理之间实现比在 JLS 中捕获的更多/不同的数据流而闻名,这可能是 javac 选择 println(Object)
的原因.在某些方面,此实现可能更接近预期的语义,并且在这个问题的示例中,常识与 javac 一致。这就是为什么恕我直言,重点应该放在改进 JLS(以及传递性 ecj)上。
编辑:虽然上面的分析是合理的,解决了问题,甚至可能与 javac 实际做的相匹配,但它无法解释,为什么问题只发生在重载解决方案之下对于 println(..)
但不在对 char[]
变量的赋值中。
在对这种差异进行更多研究之后,设计了一个替代更改,这将有效地(通过几个间接)强制编译器重新计算捕获边界,而不是像上面提议的那样传递它。此更改符合当前的 JLS。此问题的确切因果关系超出了本论坛的范围,但邀请感兴趣的各方阅读上面链接的 Eclipse 错误中的一些背景知识。
我在尝试打印 JAVA8 个收集器的结果时出现歧义错误。
我正在尝试打印 Product
对象中 ID 的总和结果,但出现以下错误:
"The method println(double) is ambiguous for the type PrintStream"
这是我遇到编译错误的一小行代码:
已编辑:添加代码片段以获取更多详细信息:
- Product.java 域 class.
包 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;
}
}
- Main.java class 我遇到编译错误的地方:
以下是我遇到编译错误的代码行:
System.out.println(productList.stream().collect(Collectors.summingDouble(x -> x.getId())));
Class 快照:
如果我将在单独的行中使用 Collector(在 println
方法之外),我不会收到任何错误。
如果我们在 println()
方法中使用它,为什么编译器无法检测到 JAVA 8 个收集器的确切 return 类型?
使用命令提示符添加另一种方法的详细信息:
我尝试使用相同 JDK 版本的命令提示符,程序编译并成功执行。所以霍尔格的回答似乎是正确的。这似乎只与 Eclipse 编译器有关:
System.out.println(productsList.stream().mapToDouble(x -> x.id).sum());
我不完全确定这里的确切代码,但是没有 必需的 类型(println
有很多重载参数)和流的通用类型,出现歧义。特别是 Double id
而不是 double
。 也许其他人可以做更好的解释。
对局部变量的赋值可能有效。
更好的是使用原始类型的流。上面使用了一个DoubleStream
。对于 "id",我宁愿期待 LongStream。
这是 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)"); }
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
at Tmp2.main(Unknown Source)
我认为,我们不需要深入研究正式的 Java 语言规范,就可以识别这种行为是不合适的。
删除两个不适用的方法,println(char[] x)
和 println(String x)
,使编译器选择正确的方法,println(Object x)
而不是 println(double x)
,但这并不令人印象深刻。
作为参考,我测试了版本 Oxygen.3a Release (4.7.3a),build 20180405-1200。可能还有其他版本受到影响。
是的,这是一个compiler bug,但仔细调查表明这可能是由于 JLS 中的一个遗漏造成的。
更具体地说,如果 JLS §18.5.2.2. 中的一个句子像这样更改,错误就会消失:
旧:
For a poly class instance creation expression or a poly method invocation expression , C contains all the constraint formulas that would appear in the set C generated by §18.5.2 when inferring the poly expression's invocation type.
仅提及"constraint forumulas"似乎还不够。
提议的新:
For a poly class instance creation expression or a poly method invocation expression , C contains all the type bounds and capture bounds that would result from reducing and incorporating the set C generated by §18.5.2 when inferring the poly expression's invocation type.
PS:Javac 以在内部和外部推理之间实现比在 JLS 中捕获的更多/不同的数据流而闻名,这可能是 javac 选择 println(Object)
的原因.在某些方面,此实现可能更接近预期的语义,并且在这个问题的示例中,常识与 javac 一致。这就是为什么恕我直言,重点应该放在改进 JLS(以及传递性 ecj)上。
编辑:虽然上面的分析是合理的,解决了问题,甚至可能与 javac 实际做的相匹配,但它无法解释,为什么问题只发生在重载解决方案之下对于 println(..)
但不在对 char[]
变量的赋值中。
在对这种差异进行更多研究之后,设计了一个替代更改,这将有效地(通过几个间接)强制编译器重新计算捕获边界,而不是像上面提议的那样传递它。此更改符合当前的 JLS。此问题的确切因果关系超出了本论坛的范围,但邀请感兴趣的各方阅读上面链接的 Eclipse 错误中的一些背景知识。