2014-03-30 82 views
16

我正在使用lambda ctx -> new SpectatorSwitcher(ctx)爲內部類構造函數創建供應商。 IntelliJ建議我改爲SpectatorSwitcher::new。 SpectatorSwitcher是一個非靜態內部類的,我在工作的類的推薦代碼編譯罰款(使用maven),但我得到以下的VerifyError上執行:內部類的構造函數引用在運行時因VerifyError失敗

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的/不是行家,而失敗編譯但仍然產生無效的字節碼?

編輯:出現的問題是比簡單的通話複雜得多,這是複製它所需的代碼:

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

你能給我們一個小的可重複的代碼片段嗎? –

+1

@SotiriosDelimanolis我不得不做更多的研究,因爲所需的代碼比我想象的要複雜得多。我添加了它。 – yawkat

+1

看起來像一個JVM錯誤。請考慮提交錯誤報告。 – 2014-03-30 09:10:32

回答

10

即使是嫌我的頭成字節碼將近一個小時後,至於爲什麼會發生這種情況,我還沒有得出合理的結論。令人意外的是,將您的方法更改爲:

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()

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

也適用。

看起來像使用構造函數引用在你的情況下不能將Test類的封閉實例傳遞給SomeClass構造函數。雖然這裏的兩種情況能夠將Test實例傳遞給SomeClass構造函數。

但我不能確切的原因。上述推理可能是錯誤的。但是,在採用這些工作方法之後,我剛剛談到這一點。

您可能想要通過lambda translation,瞭解內部工作。我仍然不太清楚lambda和方法引用是如何被翻譯的。

我發現有關類似問題的thread in lambda mailing list。另外,this SO post也有關係。

以下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等的引導程序方法。我想我們可以肯定地說這是一個錯誤。也許你可以申請一個。

+2

我已經爲此提交了一個錯誤報告。 – yawkat

+0

@yawkat請在您的問題中添加錯誤報告的鏈接。 –

+2

我沒有公共鏈接,直到它通過審查:http://s.yawk.at/HOlT – yawkat

相關問題