2014-07-01 4 views
17

La seguente classe contiene una variabile membro runnable che è inizializzata con un'istanza di una classe interna anonima. La classe interna riferimento allo stesso elemento:Perché lambda in Java 8 non consente il riferimento diretto alle variabili membro in cui le classi anonime non lo fanno?

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

Questo non è un problema, purché il metodo non viene eseguita prima che il componente sia stato assegnato e il JLS consente un tale riferimento.

La dichiarazione della variabile membro potrebbe teoricamente essere convertito in un'espressione lambda come questo:

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

Per la mia comprensione, questo è funzionalmente equivalente all'esempio precedente, ma viene respinto dal javac 1.8.0_05 con il seguente messaggio di errore:

Mentre quella affermazione è vera, non vedo perché questo non è stato consentito. Questo è stato intenzionalmente disabilitato, forse perché le espressioni lambda sono compilate con un codice byte differente che potrebbe portare a problemi se fosse permesso? O era semplicemente vietato perché c'erano già problemi con questi riferimenti quando venivano usati in classi interne anonime? O è stato inavvertitamente annullato dagli scrittori di JLS? O è un bug in javac?

+8

In eclissi, questo funziona: 'Runnable runnable =() -> System.out.println (this.runnable); 'fallisce solo senza il qualificatore' this' aggiunto. –

risposta

23

Bug #JDK-8027941 descrive esattamente questo. Dan Smith (Project Lambda Specification Lead) scrive che non è un bug, e non è limitato a lambda.

Nei commenti su a related bug report, si esprime così:

8.3.2.3: In primo luogo, una "utilizzazione" di un campo in un inizializzatore campo è generalmente vietato se l'uso avviene prima della dichiarazione di campo . Le specifiche non sono molto chiare su questo, ma l'intento è sempre stato che "prima" includesse l'inizializzatore del campo. Quindi "int x = x+1;" non è una dichiarazione di campo valida.

Egli osserva anche:

Sarebbe possibile aggiungere una caratteristica che avrebbe trattare corpi lambda appositamente, come i corpi delle classi anonimi (o, più in generale, consentire a un lambda per riferirsi a se stesso se è un inizializzatore variabile), ma questo non è stato fatto. (FWIW, un tweak semplice di 8.3.2.3 non sarebbe del tutto sicuro, proprio come il 4 ° proiettile al momento non è del tutto sicura:. "Function f = (Function) ((Function) e -> f.apply(e)).apply(null);")

Credo che il problema è che i progettisti di Java vogliono avere regole semplici e sintattiche per decidere quale tipo di affermazioni sono permesse, piuttosto che dipendere da un'analisi del codice semantica più complessa. Il vantaggio è probabilmente una specifica più semplice e quindi meno requisiti sui compilatori, mentre il costo è che i programmatori non possono esprimere ogni programma, almeno non nel modo in cui lo desiderano.


Come sottolinea Marko Topolnik, c'è una soluzione: qualificare pienamente il campo. Esempio dal rapporto bug:

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

    /* ... */ 
} 
Problemi correlati