当前位置: 首页 > 知识库问答 >
问题:

枚举、接口和(Java8)Lambdas:代码编译但在运行时失败;这是意料之中的吗?

苏胤
2023-03-14

JDK是Oracle的JDK 1.8u65,但问题也出现在“低至”1.8u25的地方。

以下是完整的SSCCE:

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);
    }
}

本代码编译;但在运行时失败:

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
// Note the <X>
Stream.<X>of(E1.INSTANCE, E2.INSTANCE).forEach(X::x);
Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());

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

共有1个答案

程博学
2023-03-14

您似乎碰到了JDK-8141508,在处理交集类型和方法引用时,它确实是javac的一个bug。它计划在Java9中得到修复。

引用Remi Forax的一封邮件:

javac在交叉类型(如lambda的目标类型和方法引用)方面有问题,通常当存在交叉类型时,javac会用交叉类型的第一种类型代替它,并在必要时添加强制转换。

public class Intersection {
      interface I {
      }
      interface J {
          void foo();
      }

      static <T extends I & J> void bar(T t) {
          Runnable r = t::foo;
      } 

      public static void main(String[] args) {
          class A implements I, J { public void foo() {} }
          bar(new A());
      }
  }
Runnable r = t -> t.foo();

我已经在某个地方见过这个bug,但无法在数据库中找到相应的bug报告:(

在您的代码中,Stream.of(e1.instance,e2.instance)创建的流类型为Stream &foo.x> ,它结合了bug的所有元素:交叉类型和方法引用。

正如Remi Forax所指出的,一个解决方案是:

Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());
 类似资料:
  • 问题内容: JDK是Oracle的JDK 1.8u65,但“低至” 1.8u25也曾出现该问题。 这是完整的SSCCE: 这段代码会编译;但在运行时失败: 用代码修复它很容易。在主要方法中,您只需要: 编辑 实际上,还有第二种方法,如公认的答案中所述…用lambda替换方法引用: 嗯 发生什么事了?为什么首先要编译初始代码?我本来希望编译器注意到方法引用不是在任何东西上,而是在上,但是没有… 我想

  • 问题内容: 我在主软件包的一个目录下有一些文件: main.go config.go server.go 当我这样做时:“执行构建”程序将完美构建并运行良好。当我这样做时:“ go run main.go”失败了。 输出: 未定义的符号是结构,并且大写,因此应将其导出。 我的Go版本:go1.1.2 linux / amd64 问题答案: 这应该工作 Go run需要一个文件或多个文件,并且它仅合

  • 我成功地编写了一个带有点精灵的标准基本变换反馈粒子系统。没有闪烁,粒子从一个缓冲区更新到下一个缓冲区,然后进行渲染,然后输出缓冲区在下一次迭代时成为输入缓冲区。所有GPU端,标准转换反馈。精彩的一个大问题是:只有在我不使用gl_PointCoord的情况下,它才有效。在我的点精灵中使用平面颜色效果很好。但我需要gl_PointCoord做任何有意义的事。我所有的着色器,不管是否使用gl_Point

  • Java8引入了重要的新语言特性,如lambda表达式。 语言中的这些变化是否伴随着编译字节码中的重大变化,从而阻止它在不使用某些反向翻译器的情况下在Java7虚拟机上运行?

  • 我写了如下界面: 和以下类 您可以看到,中的my是一个,而接口是一个字符串类型。我本以为是编译器错误,但这是如何工作的呢?