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

Generics and lambdas - different behavior in javac and Eclipse compiler

注意:我发现有多个问题指出 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>,并使用第二个参数将 U 绑定到 String。所以这个调用应该是有效的(在我看来)。

使用 javac 可以正常编译,但使用 Eclipse 无法编译:

Type mismatch: cannot convert from void to <unknown>

将类型参数添加到第一个参数 (() -> 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 编译器拒绝,但有不同的错误消息:

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

问题:同样,编译器拒绝此调用是否正确,为什么?


在我看来,这两种变体都可以通过使用类型参数在没有额外提示的情况下编译得很好。如果是这样,我将为 javac(关于 "unreported exception")填写一份错误报告,并为 Eclipse 编译器(关于 "type mismatch")填写一份错误报告。但首先我想确定规范是否同意我的观点。

使用的版本:


编辑:

我在 Eclipse 中填写了 bug 482781 问题。

javac 的问题已报告为 JDK-8056983, see

是的,你在各个方面都是对的。老实说,我无法 link 到 JLS 的特定行:类型推断 is a whole chapter.

免责声明:我使用 Eclipse Mars 4.5.1 和 JDK 1.8.0_60.

进行了测试

变体 1 应该可以编译,而 Eclipse 在这里有一个错误。我在他们的 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 中找不到任何东西,所以请随时报告!