2016-06-24 8 views
15

Ich habe eine anonyme innere Klasse und ein äquivalentes Lambda. Warum sind die Variableninitialisierungsregeln für das Lambda strenger, und gibt es eine Lösung, die sauberer ist als eine anonyme innere Klasse oder sie im Konstruktor initialisiert?Java-Lambdas haben andere variable Anforderungen als anonyme innere Klassen

import java.util.concurrent.Callable; 

public class Immutable { 
    private final int val; 

    public Immutable(int val) { this.val = val; } 

    // Works fine 
    private final Callable<String> anonInnerGetValString = new Callable<String>() {  
     @Override 
     public String call() throws Exception { 
      return String.valueOf(val); 
     } 
    }; 

    // Doesn't compile; "Variable 'val' might not have been initialized" 
    private final Callable<String> lambdaGetValString =() -> String.valueOf(val); 
} 

Edit: Ich habe über eine Abhilfe laufen: einen Getter für val verwenden.

+1

Verwandte nutzen: http://stackoverflow.com/questions/30130148/reference-to-the-final-field-from-lambda-expression –

+2

Interessanterweise, wenn Sie die 'final' Modifikator entfernen auf 'val' kompiliert es ... – lucasvw

+0

Wenn Sie' String.valueOf (val) 'in' String.valueOf (Immutable.this.val) 'es kompiliert auch – csharpfolk

Antwort

7

Das Kapitel über lambda expression bodies heißt

Anders als Code in anonymer Klasse Erklärungen erscheinen, die Bedeutung von Namen und die this und super Schlüsselwörter in einem Lambda-Körper, zusammen mit der Zugänglichkeit der referenzierten Erklärungen erscheinen, sind die gleichen wie im umgebenden Kontext (außer, dass Lambda-Parameter einführen neue Namen).

Die Transparenz der this (explizit oder implizit) in dem Körper eines lambda expression - die, zur Behandlung es die gleichen wie in dem umgebenden Kontext ist - mehr Flexibilität für Implementierungen ermöglicht, und verhindert, dass die Bedeutung von unqualifizierten Namen im Körper von abhängig von Überladung Auflösung.

Sie sind strenger deswegen. Der Umgebungskontext ist in diesem Fall eine Zuweisung zu einem Feld und das vorliegende Problem ist ein Zugriff auf ein Feld val, ein leeres Feld final, in der rechten Seite des Ausdrucks.

Die Java Language Specification heißt

Jede lokale Variable (§14.4) und jedes leere Feld final (§4.12.4, §8.3.1.2) einen eindeutig zugeordneten Wert haben muss, wenn ein Zugriff auf seine Wert tritt auf.

Ein Zugriff auf seinen Wert besteht aus den einfachen Namen der Variablen (oder für ein Feld, das einfache Name des Feldes durch this qualifiziert) überall in einem Ausdruck auftretenden außer wie der linken Operand von der einfache Zuweisungsoperator = (§15.26.1).

Für jeden Zugriff auf eine lokale Variable oder leere final Feld x muss x auf jeden Fall vor dem Zugriff zugewiesen sein, oder ein Fehler bei der Kompilierung auftritt.

Es geht dann auf

Lassen C eine Klasse sein, zu sagen, und lassen Sie Vstatic Mitglied Feld in C von C, erklärt ein leeres final nicht sein. Dann gilt:

  • V ist auf jeden Fall nicht zugeordneten (und darüber hinaus nicht eindeutig zugeordnet ist) vor der am weitesten links liegende Instanz Initialisierer (§8.6) oder Instanzvariable Initialisierer von C.

  • V ist [un] zugewiesen vor einer Instanz Initialisierer oder Instanzvariable Initialisierer von C andere als die am weitesten links stehenden iff V [un] nach dem vorhergehenden Beispiel Initialisierer oder Instanz Variableninitialisierer von C zugeordnet ist.

Ihr Code sieht im Grunde wie diese

private final int val; 
// leftmost instance variable initializer, val still unassigned 
private final Callable<String> anonInnerGetValString = ... 
// still unassigned after preceding variable initializer 
private final Callable<String> lambdaGetValString = ... 

Der Compiler bestimmt daher, dass val in nicht zugeordnet, wenn sie den Zugang innerhalb der Initialisierungsausdruck ist für lambdaGetValString. Die obigen Regeln gelten für die Verwendung eines einfachen Namens, val, nicht für einen qualifizierten Ausdruck, this.val. Sie können

final Callable<String> lambdaGetValString =() -> String.valueOf(this.val); 
3

kompilieren Dies wird nicht:

public class Example 
{ 
    private final int x; 
    private final int y = 2 * x; 

    public Example() { 
    x = 10; 
    } 
} 

aber dies wird:

public class Example 
{ 
    private final int x; 
    private final int y; 

    public Example() { 
    x = 10; 
    y = 2 * x; 
    } 
} 

und diese so wird:

public class Example 
{ 
    private final int x = 10; 
    private final int y = 2 * x; 
} 

es ist also nichts mit Lambda-Ausdrücke zu tun. Ein Feld, das in derselben Zeile initialisiert wird, in der es deklariert ist, wird vor der Ausführung des Konstruktors ausgewertet. An diesem Punkt wurde die Variable 'val' (oder in diesem Beispiel 'x') nicht initialisiert.

+0

Definieren _same line_. –

Verwandte Themen