2014-07-01 6 views
17

Die folgende Klasse enthält eine Elementvariable runnable, die mit einer Instanz einer anonymen inneren Klasse initialisiert wird. Die innere Klasse verweist dasselbe Mitglied:Warum verbieten Lambdas in Java 8 Vorwärtsverweise auf Member-Variablen, bei denen anonyme Klassen dies nicht tun?

class Example { 
    Runnable runnable = new Runnable() { 
     @Override 
     public void run() { 
      System.out.println(runnable); 
     } 
    }; 
} 

Dies ist kein Problem, solange das Verfahren nicht vor dem Elemente zugeordnet wurde ausgeführt und das JLS ermöglicht so eine Referenz. von javac 1.8.0_05 mit folgendem

Runnable runnable =() -> System.out.println(runnable); 

Zu meinem Verständnis, das dem vorherigen Beispiel funktional äquivalent ist, aber es wird abgelehnt:

Die Deklaration der Membervariable theoretisch auf einen Lambda-Ausdruck wie folgt umgewandelt werden könnte Fehlermeldung:

Error:(2, 54) java: self-reference in initializer 

Während diese Aussage wahr ist, sehe ich nicht, warum dies nicht erlaubt war. War dies absichtlich nicht erlaubt, vielleicht weil Lambda-Ausdrücke zu verschiedenen Byte-Codes kompiliert wurden, was zu Problemen führen würde, wenn es erlaubt wäre? Oder wurde es einfach nicht erlaubt, weil es schon Probleme mit diesen Referenzen gab, als sie in anonymen inneren Klassen verwendet wurden? Oder wurde es von den JLS-Autoren unbeabsichtigt abgelehnt? Oder ist es ein Fehler in javac?

+8

In Eclipse funktioniert dies: 'Runnable Runnable =() -> System.out.println (this.runnable); 'Es scheitert nur ohne das hinzugefügte' this'-Qualifikationsmerkmal. –

Antwort

23

Bug #JDK-8027941 beschreibt genau dies. Dan Smith (Project Lambda Specification Lead) schreibt, dass es sich nicht um einen Bug handelt und nicht nur um Lambdas.

In den Kommentaren auf a related bug report, legt er es wie folgen aus:

8.3.2.3: Zunächst wird ein „Verwendung“ ein Feldes in einem Feldinitialisierer ist generell verboten, wenn die Verwendung vor der Erklärung Feld auftritt . Die Spezifikation ist nicht sehr klar, aber die Absicht war schon immer, dass "vorher" den eigenen Initialisierer des Feldes enthält. So ist "int x = x+1;" keine gültige Felddeklaration.

Er bemerkt auch:

Es wäre möglich hinzufügen eine Funktion, die Lambda-Körper speziell, wie Körper von anonymen Klassen (oder, allgemeiner ausgedrückt, ermöglichen eine Lambda beziehen sich auf sich selbst behandeln würden wenn es ein variabler Initialisierer ist), aber dies wurde nicht gemacht. (FWIW, eine einfache zwicken von 8.3.2.3 nicht ganz sicher sein, genau wie die vierte Kugel ist momentan nicht ganz sicher. „Function f = (Function) ((Function) e -> f.apply(e)).apply(null);“)

Ich denke, das Problem ist, dass die Entwickler von Java wollen haben einfache, syntaktische Regeln, um zu entscheiden, welche Art von Aussagen erlaubt sind, anstatt von komplexeren semantischen Code-Analysen abhängig zu sein. Der Vorteil ist wahrscheinlich eine einfachere Spezifikation und damit geringere Anforderungen an Compiler, während die Kosten dafür sorgen, dass Programmierer nicht jedes Programm ausdrücken können - zumindest nicht so, wie sie es wollen.


Wie Marko Topolnik hervorhebt, gibt es eine Lösung: das Feld vollständig zu qualifizieren. Beispiel aus dem Fehlerbericht:

import java.util.function.Function; 

public class LambdaSelfRef { 

    // COMPILATION FAILURE 
    public static Function<Object, Object> op1 = e -> op1.apply(e); 

    // COMPILES OK 
    public static Function<Object, Object> op2 = e -> LambdaSelfRef.op2.apply(e); 

    /* ... */ 
}