Constructor reference for inner class fails with VerifyError at runtime

后端 未结 1 1476
耶瑟儿~
耶瑟儿~ 2021-02-05 11:39

I am creating a supplier for an inner class constructor using the lambda ctx -> new SpectatorSwitcher(ctx). IntelliJ suggested that I change it to Spectato

相关标签:
1条回答
  • 2021-02-05 12:23

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

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

    also works.

    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.

    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.

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

    The following runtTest() method:

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

    Is compiled to following bytecode:

      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
    

    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.

    0 讨论(0)
提交回复
热议问题