首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >接口和(Java 8) lambdas:代码编译但在运行时失败;这是预期的吗?

接口和(Java 8) lambdas:代码编译但在运行时失败;这是预期的吗?
EN

Stack Overflow用户
提问于 2015-11-26 04:09:25
回答 1查看 2.2K关注 0票数 24

JDK是Oracle的JDK 1.8u65,但“低至”1.8u25也存在这个问题。

下面是完整的SSCCE:

代码语言:javascript
复制
public final class Foo
{
    private interface X
    {
        default void x()
        {
        }
    }

    private enum E1
        implements X
    {
        INSTANCE,
        ;
    }

    private enum E2
        implements X
    {
        INSTANCE,
        ;
    }

    public static void main(final String... args)
    {
        Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(X::x);
    }
}

这段代码可以编译,但在运行时失败:

代码语言:javascript
复制
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
    at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
    at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
    at com.github.fge.grappa.debugger.main.Foo.main(Foo.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Enum; not a subtype of implementation type interface com.github.fge.grappa.debugger.main.Foo$X
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
    at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
    ... 8 more

在代码中修复它是“容易的”;在main方法中,您只需:

代码语言:javascript
复制
// Note the <X>
Stream.<X>of(E1.INSTANCE, E2.INSTANCE).forEach(X::x);

EDIT实际上还有第二种方法,就像公认的答案中提到的那样……将方法引用替换为lambda:

代码语言:javascript
复制
Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());

所以,嗯。这里发生了什么?为什么要首先编译初始代码?我本以为编译器会注意到方法引用不是在任何Enum<?>上,而是在X上,但没有...

我遗漏了什么?这是编译器中的错误吗?对我的误解?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-11-26 04:49:09

看起来你遇到了JDK-8141508,在处理交叉点类型和方法引用时,这确实是javac的一个缺陷。计划在Java 9中修复该问题。

引用a mail from Remi Forax

假设我们有这样的代码,

公共类交集{接口I {}接口J{ void foo();}静态 void bar(T t) { Runnable r= t::foo;}公共静态void main(String[]参数){类A实现I,J{ public void foo() {}} bar(new A());}}

目前,javac在J::foo上生成一个方法引用,并使用一个带I作为参数的invokedynamic,因此它在运行时失败。javac应该把t::foo去糖成一个lambda,这个lambda接受一个I,然后添加一个强制转换到J,就像调用一个交叉点类型的方法一样。

因此,解决方法是使用lambda,

Runnable r=t -> t.foo();

我已经在某个地方看到了这个错误,但在数据库中找不到相应的错误报告:(

在您的代码中,Stream.of(E1.INSTANCE, E2.INSTANCE)创建的流的类型是Stream<Enum<?>&Foo.X>,它结合了bug的所有元素:交集类型和方法引用。

正如Remi Forax所指出的,一种变通方法是:

代码语言:javascript
复制
Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());

即使用显式lambda表达式而不是方法引用。

票数 22
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33925551

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档