2014-03-25 7 views
23

Während ich einige grundlegende Lambda-Übungen machte, gab mir die Ausgabe einer scheinbar identischen anonymen inneren Klasse eine andere Ausgabe als das Lambda.Lambda verhält sich anders als anonyme innere Klasse

interface Supplier<T> { 

    T get(T t); 
} 

Szenario # 1

Supplier<Integer> s1 = new Supplier<Integer>() { 
    @Override 
    public Integer get(Integer t) { 
     return t; 
    } 
}; 
Supplier<Integer> s2 = t -> t; 
System.out.println(s1.get(2)); 
System.out.println(s2.get(2)); 

Ausgänge und . Nichts Neues hier.


Aber wenn ich dies tun:

Szenario # 2

Supplier<Integer> s1 = new Supplier<Integer>() { 
    @Override 
    public Integer get(Integer t) { 
     return t++; 
    } 
}; 
Supplier<Integer> s2 = t -> t++; 
System.out.println(s1.get(2)); 
System.out.println(s2.get(2)); 

Ausgänge und

FRAGE: Sollte nicht beide Ausgänge sein identisch? Fehle ich etwas?


Aus Gründen der Vollständigkeit halber: Szenario # 3

Supplier<Integer> s1 = new Supplier<Integer>() { 
    @Override 
    public Integer get(Integer t) { 
     return ++t; 
    } 
}; 
Supplier<Integer> s2 = t -> ++t; 
System.out.println(s1.get(2)); 
System.out.println(s2.get(2)); 

Ausgänge und . Auch hier nichts Neues.

UPDATE: Noch immer gleiche Ausgabe von 1.8.0-B132

UPDATE # 2: Fehlerbericht:https://bugs.openjdk.java.net/browse/JDK-8038420

UPDATE # 3: Der Fehler in Javac wird behoben, Sie sollte jetzt das gleiche Ergebnis erzielen können.

+1

ich 2 und 2 für die zweiten Code-Schnipsel. –

+1

+1 für kann nicht replizieren –

+1

Nun, das ist interessant. Schauen wir uns hier etwas an, das mit JVM-Versionen/-Builds zu tun hat? Ich verwende Build 1.8.0-ea-b81. Derzeit wird eine neuere Version heruntergeladen. Wird Updates posten. – victorantunes

Antwort

13

Nach Bytecode erzeugt:

Java (TM) SE Runtime Environment (build 1.8.0-B132)

Lambda:

private static java.lang.Integer lambda$main$0(java.lang.Integer); 
    descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer; 
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC 
    Code: 
    stack=2, locals=2, args_size=1 
     0: aload_0 
     1: invokevirtual #9     // Method java/lang/Integer.intValue:()I 
     4: iconst_1 
     5: iadd 
     6: invokestatic #6     // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
     9: dup 
     10: astore_0 
     11: astore_1 
     12: aload_0 
     13: areturn 
    LineNumberTable: 
     line 20: 0 
    LocalVariableTable: 
     Start Length Slot Name Signature 
      0  14  0  t Ljava/lang/Integer; 

Anonymous Klasse:

public java.lang.Integer get(java.lang.Integer); 
    descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer; 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=4, args_size=2 
     0: aload_1 
     1: astore_2 
     2: aload_1 
     3: invokevirtual #2     // Method java/lang/Integer.intValue:()I 
     6: iconst_1 
     7: iadd 
     8: invokestatic #3     // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
     11: dup 
     12: astore_1 
     13: astore_3 
     14: aload_2 
     15: areturn 
     LineNumberTable: 
     line 16: 0 
     LocalVariableTable: 
     Start Length Slot Name Signature 
      0  16  0 this LTest$1; 
      0  16  1  t Ljava/lang/Integer; 

Wie Sie Variable aus lokaler Variablentabelle (Methodenparameter t) Laufzeitspeicher Kopie des Parameters in einer anderen Variablen (astore_2) und verwenden Sie dann diese Kopie des Parameters nach dem Laden als Rückkehr in anonymer Klasse sehen Wert.

Lambda-Methode erstellt keine Kopie des Parameters (Laden -> Entpacken -> 1 -> Box -> Speichern -> Laden -> Zurück).

UPDATE

Es ist definitiv ein Javac Fehler.

erhielt ich Quelle von http://hg.openjdk.java.net/jdk8u/jdk8u

anonyme Klasse und Lambda umwandelt folgende Zwischendarstellungen:

@Override() 
public Integer get(Integer t) { 
    return (let /*synthetic*/ final Integer $112619572 = t in 
     (let /*synthetic*/ final Integer $1295226194 = t = Integer.valueOf((int)(t.intValue() + 1)) in $112619572)); 
} 

/*synthetic*/ private static Integer lambda$main$0(final Integer t) { 
    return (let /*synthetic*/ final Integer $1146147158 = t = Integer.valueOf((int)(t.intValue() + 1)) in t); 
} 

in Lambda erzeugten Methodenparameter als endgültig gekennzeichnet, da LambdaToMethod Setzer markiert alle Parameter als FINAL (entsprechend Quellcode LambdaTranslationContext.translate (...): 1899).

lassen Dann Expression Builder überprüft variable Flaggen und wenn, wenn es endgültig auslässt temporäre Variable Generation ist (nach Quellcode Lower.abstractRval (...): 2277), da Modifikationen betrachtet verboten.

Mögliche Lösungen:

  1. Verbieten Parameteränderung innerhalb Lambda oder
  2. FINAL Flag aus lokaler Variable entfernen (LambdaTranslationContext.translate (...): 1894) und Parameter (LambdaTranslationContext.translate (...): 1899) in lamda erzeugt Methode:

    case LOCAL_VAR: 
        ret = new VarSymbol(FINAL, name, types.erasure(sym.type), translatedSym); 
    ... 
    
    case PARAM: 
        ret = new VarSymbol(FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym); 
    ... 
    

I entfernt FINAL Flagge und bekam erwartete Ergebnisse auf Tests von: https://bugs.openjdk.java.net/browse/JDK-8038420

+0

Obwohl es keine offenkundige offizielle Wort in der Sache ist, akzeptiere ich dies als die richtige Antwort. Ich schätze, ich sollte einen Fehlerbericht einreichen. Kann mir jemand darauf hinweisen, wie man das macht? – victorantunes

+0

http://mail.openjdk.java.net/pipermail/lambda-dev/- Mailingliste openJDK Lambda-dev –

+0

https://bugs.openjdk.java.net/secure/Dashboard.jspa – Michael