2016-06-24 54 views
15

Ho una classe interna anonima e un lambda equivalente. Perché le regole di inizializzazione delle variabili sono più rigide per il lambda, e c'è una soluzione più pulita di una classe interna anonima o inizializzata nel costruttore?I lambda Java hanno requisiti variabili diversi rispetto alle classi interne anonime

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: mi imbatto una soluzione: utilizzare un getter per val.

+1

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

+2

È interessante notare che, se si rimuove il modificatore 'final' su 'val' compila ... – lucasvw

+0

Quando cambi' String.valueOf (val) 'in' String.valueOf (Immutable.this.val) 'compila anche – csharpfolk

risposta

7

Il capitolo sulla lambda expression bodies afferma

A differenza di codice appaiono nelle dichiarazioni di classe anonimi, il significato di nomi e la this e super le parole chiave che appaiono in un corpo lambda, insieme con l'accessibilità delle dichiarazioni di riferimento, sono gli stessi come nel contesto circostante (eccetto che i parametri lambda introducono i nuovi nomi ).

La trasparenza this (esplicite ed implicite) nel corpo di un'espressione lambda - cioè trattando lo stesso come nella contesto circostante - consente maggiore flessibilità per implementazioni, e impedisce sensi qualificato i nomi nel corpo da dipendono dalla risoluzione di sovraccarico.

Sono più severi per questo.

Il contesto circostante, in questo caso, è un'assegnazione a un campo e il problema in questione è un accesso di un campo, val, un campo vuoto final, nella parte destra dell'espressione.

Il linguaggio Java Specification afferma

Ogni variabile locale (§14.4) ed ogni vuoto final campo (§4.12.4, §8.3.1.2) deve avere un valore decisamente assegnato quando qualsiasi accesso del suo valore si verifica.

Un accesso al suo valore consiste nella semplice nome della variabile (o, per un campo, il semplice nome del campo qualificato da this) verificano ovunque in un'espressione eccetto come operando sinistro di l'operatore di assegnazione semplice = (§15.26.1).

Per ogni accesso di una variabile locale o vuoto final campo x, x deve essere definitivamente assegnato prima che l'accesso, o in fase di compilazione si verifica un errore.

Si passa poi a dire

Diamo C essere una classe, e sia V essere un vuoto final non static campo membro di C, ha dichiarato in C. Poi:

  • V è sicuramente non assegnato (ed inoltre non è sicuramente assegnato) prima della sinistra esempio inizializzatore (§8.6) o istanza variabile inizializzatore di C.

  • V è [un] assegnato prima un inizializzatore istanza o un'istanza inizializzazione variabile C diversa dalla sinistra sse V è [un] assegnato dopo il precedente esempio inizializzatore o un'istanza inizializzazione variabile C.

il codice sia sostanzialmente in questo modo

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 = ... 

Il compilatore determina, pertanto, che in val assegnato quando è accesso all'interno dell'espressione di inizializzazione per lambdaGetValString.

Le regole sopra riportate si applicano all'uso di un nome semplice, val, non a un'espressione qualificata, this.val. È possibile utilizzare

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

Questo non compilerà:

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

    public Example() { 
    x = 10; 
    } 
} 

ma questo sarà:

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

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

e così sarà questo:

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

Quindi non è niente a che fare con lambda. Un campo inizializzato sulla stessa riga in cui è stato dichiarato viene valutato prima dell'esecuzione del costruttore. Quindi a quel punto, la variabile 'val' (o in questo esempio 'x') non è stata inizializzata.

+0

Definisci _some line_. –