内部类的构造函数引用在运行时因VerifyError而失败 [英] Constructor reference for inner class fails with VerifyError at runtime

查看:155
本文介绍了内部类的构造函数引用在运行时因VerifyError而失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用lambda ctx - >创建内部类构造函数的供应商。新的SpectatorSwitcher(ctx)。 IntelliJ建议我改为 SpectatorSwitcher :: new 。 SpectatorSwitcher是我正在使用的类的非静态内部类。建议的代码编译得很好(使用maven)但我在执行时得到以下VerifyError:

I am creating a supplier for an inner class constructor using the lambda ctx -> new SpectatorSwitcher(ctx). IntelliJ suggested that I change it to SpectatorSwitcher::new instead. SpectatorSwitcher is a non-static inner class of the class I'm working in. The suggested code compiles fine (using maven) but I get the following VerifyError on execution:

Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    Test.lambda$runTest$8(LTest$Worker;)V @2: invokedynamic
  Reason:
    Type 'Test$Worker' (current frame, stack[1]) is not assignable to 'Test'
  Current Frame:
    bci: @2
    flags: { }
    locals: { 'Test$Worker' }
    stack: { 'Test$Worker', 'Test$Worker' }
  Bytecode:
    0000000: 2a2a ba00 0b00 00b6 000c b1            

    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2688)
    at java.lang.Class.getMethod0(Class.java:2937)
    at java.lang.Class.getMethod(Class.java:1771)
    at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)

为什么javac / maven在编译时没有失败但仍然产生无效的字节码?

Why is javac / maven not failing while compiling but still producing invalid byte code?

编辑:问题似乎比简单调用复杂得多,这是重现它所需的代码:

The problem appears to be far more complex than the simple call, this is the code needed to reproduce it:

import java.util.function.Function;

/**
 * @author Yawkat
 */
public class Test {
    public static void main(String[] args) { new Test().runTest(); }

    private void runTest() {
        Worker worker = new Worker();
        run(() -> worker.print(field -> new SomeClass(field)));
        run(() -> worker.print(SomeClass::new));
    }

    private void run(Runnable runnable) {
        runnable.run();
    }

    private class SomeClass {
        final Object field;

        SomeClass(Object field) {
            this.field = field;
        }
    }

    private static class Worker {
        void print(Function<Object, Object> i) {
            System.out.println(i.apply(null));
        }
    }
}


推荐答案

即使把我的头撞到字节码中将近一个小时,我也无法得出一个合理的结论,为什么会发生这种情况。令人惊讶的是,将您的方法更改为:

Even after smacking my head into the bytecode for almost an hour, I've not been able to come to a reasonable conclusion as to why this is happening. Surprisingly, changing your method to this:

private void runTest() {
    Worker worker = new Worker();
    run(() -> worker.print(field -> new SomeClass(field)));
    Function<Object, Object> function = SomeClass::new;
    run(() -> worker.print(function));
}

工作正常。另外,摆脱 run()方法调用,只需调用 worker.print()

works fine. Also, getting rid of run() method invocation, and just calling worker.print():

private void runTest() {
    Worker worker = new Worker();
    worker.print(field -> new SomeClass(field));
    worker.print(SomeClass::new);
}

也有效。

似乎在你的情况下使用构造函数引用无法传递 Test 的封闭实例类到 SomeClass 构造函数是必需的。虽然这里的两个案例能够将 Test 实例传递给 SomeClass 构造函数。

It seems like, using the constructor reference as in your case is not able to pass the enclosing instance of Test class to the SomeClass constructor which is required. While the two cases here are able to pass Test instance to the SomeClass constructor.

但我无法说出具体原因。上述推理可能是错误的。但是在完成这些工作方法后我才刚刚谈到这一点。

But I couldn't come to the exact reason. The above reasoning might very well be wrong. But I've just come to that after getting to those working approach.

你可能想要通过 lambda translation ,了解内部工作。我还不太清楚如何翻译lambdas和方法引用。

You might want to go through lambda translation, to understand the inner working. I'm still not very much clear about how lambdas and method references are translated.

我找到了 lambda邮件列表中的线程关于类似问题。另外,此SO帖子也是相关的。

I found a thread in lambda mailing list about similar issue. Also, this SO post is also related.

以下 runtTest()方法:

public void runTest() {
    Worker worker = new Worker();
    run(() -> worker.print((field) -> new SomeClass(field)));
    run(() -> worker.print(SomeClass::new));

    Function<Object, Object> func = SomeClass::new;
    run(() -> worker.print(func));

    worker.print(SomeClass::new);
}

编译为以下字节码:

  public void runTest();
    Code:
       0: new           #2                  // class SO$Worker
       3: dup
       4: invokespecial #3                  // Method SO$Worker."<init>":()V
       7: astore_1
       8: aload_0
       9: aload_0
      10: aload_1
      11: invokedynamic #4,  0              // InvokeDynamic #0:run:(LSO;LSO$Worker;)Ljava/lang/Runnable;
      16: invokevirtual #5                  // Method run:(Ljava/lang/Runnable;)V
      19: aload_0
      20: aload_1
      21: invokedynamic #6,  0              // InvokeDynamic #1:run:(LSO$Worker;)Ljava/lang/Runnable;
      26: invokevirtual #5                  // Method run:(Ljava/lang/Runnable;)V
      29: aload_0
      30: invokedynamic #7,  0              // InvokeDynamic #2:apply:(LSO;)Ljava/util/function/Function;
      35: astore_2
      36: aload_0
      37: aload_1
      38: aload_2
      39: invokedynamic #8,  0              // InvokeDynamic #3:run:(LSO$Worker;Ljava/util/function/Function;)Ljava/lang/Runnable;
      44: invokevirtual #5                  // Method run:(Ljava/lang/Runnable;)V
      47: aload_1
      48: aload_0
      49: invokedynamic #7,  0              // InvokeDynamic #2:apply:(LSO;)Ljava/util/function/Function;
      54: invokevirtual #9                  // Method SO$Worker.print:(Ljava/util/function/Function;)V
      57: return

我只能看到第二个 run()方法调用未通过 LSO 参数,而其他人确实通过了它。您可以运行命令 - javap -c -s -verbose Test ,查看#0的引导方法#1 等我想我们可以肯定地说这是一个错误。也许你可以提交一个。

I can see only the second run() method invocation doesn't pass LSO argument, while others do pass it. You can run the command - javap -c -s -verbose Test, to see Bootstrap methods for #0, #1, etc. I guess we can definitely say that this is a bug. Perhaps you can file one.

这篇关于内部类的构造函数引用在运行时因VerifyError而失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆