2015-01-04 12 views
6

Ho trovato this article molto informativo rispetto alle funzioni vecchio stile alle nuove funzioni lambda Java-8 e all'elaborazione parallela. Una cosa che non riuscivo a capire era uno di restrizione sulle lambda-funzioni: Da pagina 4:Come si può fare riferimento a un'espressione lambda Java?

3.3 Presupposti Anche se le espressioni lambda sono intese come una maggiore con- alternativa CISE al AIC , sono non una sostituzione completa. Ci sono diversi presupposti che controlli LambdaFicator prima di refactoring di AIC in un'espressione lambda. Queste precondizioni sono inerenti al modo in cui le espressioni lambda sono implementate in Java, non alle limitazioni del nostro strumento. (P1) AIC deve creare un'istanza da un'interfaccia. Le istanze di classi astratte o concrete non possono essere convertite in espressioni lambda . (P2) AIC non deve contenere campi e dichiara solo un metodo. Un'espressione lambda rappresenta una singola funzione anonima - zione; pertanto, un AIC con più metodi non può essere convertito in una singola espressione lambda. (P3) AIC non deve avere riferimenti a questo o super . In un'espressione lambda, questo e eccellente sono scope lessicale, nel senso che sono interpretati proprio come lo sarebbero in un ambiente che racchiude , ad esempio, come se sono apparsi nella dallo Stato mento prima che l'espressione lambda [ 6]. Tuttavia, in un si riferiscono alla classe interna stessa. (P4) AIC non deve dichiarare un metodo ricorsivo. Per eseguire la chiamata ricorsiva, è necessario ottenere un riferimento alla funzione anonima . Mentre LambdaFicator potrebbe eseguire questo refactoring, questo potrebbe introdurre la complessità non necessaria nel codice e danneggiare la comprensibilità.

In P4, "AIC non deve dichiarare un metodo ricorsivo ... LambdaFicator potrebbe eseguire questo refactoring ...", come si potrebbe refactoring un'espressione lambda per fare riferimento a se stesso? Poiché per definizione queste funzioni anonime lambda non hanno un nome che può essere referenziato e non hanno un riferimento a se stessi (P3 sopra).?

risposta

2
public class Test { 
    static Runnable r; 

    public static void main(String... args) { 
     r =() -> r.run(); 
     r.run(); 
    } 
} 

Il Runnable ottiene un riferimento ad essa dal campo r quando viene eseguito.

È anche possibile utilizzare una matrice di lunghezza 1 per memorizzare il riferimento se non si desidera aggiungere un campo.

1

Derivato da @ risposta di Alex:

@FunctionalInterface 
public interface SelfRunnable extends Runnable { 
    public void run(SelfRunnable this_); 

    @Override 
    public default void run() { 
    run(this); 
    } 

    public static Runnable runnable(SelfRunnable runnable) { 
    return runnable; 
    } 
} 

public interface Test { 
    public static void main(String... arguments) { 
    final Runnable r = SelfRunnable.runnable(this_ -> this_.run()); 
    r.run(); 
    } 
} 
1

Come detto here, modo canonico di Java per implementare una funzione ricorsiva è un metodo:

public static int fib(int n) { 
    return n==0? 0: n==1? 1: fib(n-1)+fib(n-2); 
} 

Quindi, se avete bisogno di un'istanza di adempiere a funzionaleinterface è possibile utilizzare un metodo di riferimento:

Function<Integer, Integer> fib = MyClass::fib; 

o

IntUnaryOperator fib0=MyClass::fib; 

Questo è l'equivalente più di un'espressione lambda come un'espressione lambda non è zucchero sintattico per una classe runtime generato sostituendo la classe interna anonima ma anche un metodo anonimo ospitare il codice del singolo metodo astratto da implementare.

L'utilizzo di un metodo ricorsivo ordinario trasforma il metodo anonimo in un nome, mantenendo tutte le altre proprietà delle espressioni lambda. Questo differisce da tutti gli altri metodi per tentare di dare a un'espressione lambda un riferimento a se stesso, come la memorizzazione dell'istanza in un campo. Queste soluzioni alternative non sono semanticamente equivalenti (e meno efficienti).

Problemi correlati