泛型和lambdas - javac和Eclipse编译器中的不同行为

时间:2015-11-21 19:54:16

标签: java java-8 javac eclipse-jdt

注意:我发现了多个问题,指出了javac和Eclipse编译器之间的差异,但据我所知,所有这些问题都讨论了其他问题。

假设我们有这个方法:

public static <T, U> void foo(Supplier<T> a, Function<T, U> b, Consumer<U> c)
{
    c.accept(b.apply(a.get()));
}

我在编译对此方法的调用时发现javac和Eclipse Java编译器之间存在不同的行为,我不确定哪两个是正确的。

这种方法的简单用法可能是:

// variant 1
foo(
    () -> Optional.of("foo"),
    value -> value.get(),
    value -> System.out.println(value));

编译器应该能够使用第一个参数将T绑定到Optional<String>,使用第二个参数绑定UString。所以这个电话应该是有效的(在我看来)。

这可以使用javac进行编译,但无法使用Eclipse进行编译:

  

类型不匹配:无法从void转换为&lt; unknown&gt;

在第一个参数(() -> Optional.<String> of("foo"))中添加一个类型参数也使它在Eclipse中编译。

问题:从规范的角度来看,Eclipse是否正确拒绝此调用(以及为什么(不))?

现在假设我们想要抛出一个自定义(运行时)异常,如果Optional为空:

// variant 2
foo(
    () -> Optional.of("foo"),
    value -> value.orElseThrow(() -> new RuntimeException()),
    value -> System.out.println(value));

这被javac和Eclipse编译器拒绝,但是有不同的错误消息:

  • javac:&#34;未报告的例外X;必须被抓住或宣布被抛出&#34;
  • Eclipse编译器:&#34;类型不匹配:无法从void转换为&lt; unknown&gt;&#34;

当我将type参数添加到第一个参数时,Eclipse成功编译,而javac仍然失败。当我将<RuntimeException>作为类型参数添加到第二个参数时,反过来说,Eclipse失败并且javac成功。

问题:再次,编译器是否正确拒绝此调用以及为什么?

在我看来,这两个变体应该通过使用类型参数进行编译而没有额外的提示。如果是这样,我将填写javac的一个错误报告(关于&#34;未报告的异常&#34;)和一个用于Eclipse编译器的错误报告(关于&#34;类型不匹配&#34;)。但首先我想确定规范与我的观点相同。

使用的版本:

  • javac:1.8.0_66
  • Eclipse JDT:3.11.1.v20151118-1100

修改

我在Eclipse中填写了bug 482781来解决这个问题。

javac的问题已报告为JDK-8056983,请参阅Tunakis answer

1 个答案:

答案 0 :(得分:5)

是的,你在每个方面都是正确的。老实说,我无法链接到JLS的特定行:类型推断is a whole chapter

免责声明:我使用Eclipse Mars 4.5.1和JDK 1.8.0_60进行了测试。

Variant 1应该编译,Eclipse在这里有一个bug。我在他们的Bugzilla中找不到与此相关的任何内容,因此您可以继续进行归档。你可以向自己保证,如果你将你的例子减少到它,它应该编译:

public static <T> void foo(Supplier<T> a) {
    a.get();
}

foo(() -> Optional.of("foo"));

使用Eclipse和javac编译都很好。添加参数确实(应该)不会在编译期间更改T推断的类型。

变体2不为javac编译,这确实是一个错误,如JDK-8056983中所述。编译器应该能够推断XRuntimeException。至于为什么Eclipse仍然无法编译,再次,我在他们的Bugzilla中找不到任何东西,所以随时报告这个!