2014-03-30 7 views
16

Ich erstelle einen Lieferanten für einen inneren Klassenkonstruktor mit dem Lambda ctx -> new SpectatorSwitcher(ctx). IntelliJ schlug vor, dass ich stattdessen SpectatorSwitcher::new ändere. SpectatorSwitcher ist eine nicht-statische innere Klasse der Klasse in Ich arbeite Der vorgeschlagene Code fein kompiliert (mit Maven), aber ich bekomme die folgende VerifyError auf Ausführung.Konstruktorreferenz für innere Klasse schlägt mit VerifyError zur Laufzeit fehl

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) 

Warum versagt javac/Maven nicht während Kompilieren, aber immer noch produzieren ungültigen Byte-Code?

Edit: Das Problem weitaus komplexer als die einfachen Aufruf zu sein scheint, dies ist der Code benötigt es zu reproduzieren:

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

Können Sie uns ein kleines reproduzierbares Code-Snippet geben? –

+1

@SotiriosDelimanolis Ich musste noch mehr recherchieren, weil der benötigte Code viel komplexer ist, als ich dachte. Ich habe es hinzugefügt. – yawkat

+1

Sieht aus wie ein JVM-Fehler. Bitte überlege, einen Fehlerbericht einzureichen. –

Antwort

10

Auch nach meinem Kopf in den Bytecode für fast eine Stunde Schmatzen, Ich konnte nicht zu einer vernünftigen Schlussfolgerung kommen, warum dies geschieht. Überraschenderweise ändern Sie Ihre Methode zu diesem:

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

funktioniert gut. Auch run() Methodenaufruf los zu werden, und nur anrufen worker.print():

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

auch funktioniert.

Es scheint, als ob die Verwendung der Konstruktorreferenz in Ihrem Fall die umschließende Instanz der Klasse Test nicht an den benötigten Konstruktor SomeClass übergeben kann. Während die beiden Fälle hier Test Instanz an den SomeClass Konstruktor übergeben können.

Aber ich konnte nicht zum genauen Grund kommen. Die obige Argumentation könnte sehr falsch sein. Aber ich bin gerade dazu gekommen, nachdem ich zu diesen Arbeitsmethoden gekommen bin.

Sie könnten durch lambda translation gehen, um die innere Arbeit zu verstehen. Ich weiß immer noch nicht genau, wie Lambda und Methodenbezeichnungen übersetzt werden.

Ich fand eine thread in lambda mailing list über ähnliche Problem. Auch this SO post ist ebenfalls verwandt.

Die folgende runtTest() Methode:

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

wird zu folgenden Bytecode kompiliert:

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 

kann ich nur die zweite run() Methodenaufruf sehen nicht LSO Argument nicht besteht, während andere es tun passieren . Sie können den Befehl - javap -c -s -verbose Test ausführen, um Bootstrap-Methoden für #0, #1 usw. zu sehen. Ich denke, wir können definitiv sagen, dass dies ein Fehler ist. Vielleicht können Sie einen einreichen.

+2

Ich habe einen Fehlerbericht dafür eingereicht. – yawkat

+0

@yawkat Bitte fügen Sie den Link zum Fehlerbericht in Ihrer Frage hinzu. –

+2

Ich habe keine öffentliche Verbindung, bis es Prüfung bestanden hat: http://s.yawk.at/HoLT – yawkat

Verwandte Themen