2012-11-28 4 views
217

In Java 8, i metodi possono essere creati come espressioni Lambda e possono essere passati per riferimento (con un po 'di lavoro sotto il cofano). Ci sono un sacco di esempi online con lambdas creati e usati con metodi, ma non ci sono esempi su come fare un metodo prendendo un lambda come parametro. Qual è la sintassi per questo?Come si definisce un metodo che accetta un lambda come parametro in Java 8?

MyClass.method((a, b) -> a+b); 


class MyClass{ 
    //How do I define this method? 
    static int method(Lambda l){ 
    return l(5, 10); 
    } 
} 
+15

Buona domanda. E hai ragione: nessuna delle esercitazioni contiene quella parte. – Martin

risposta

157

Lambda sono puramente un call-sito costrutto: il destinatario del lambda non ha bisogno di sapere che un Lambda è coinvolto, invece accetta un'interfaccia con il metodo appropriato.

In altre parole, si definisce o si utilizza un'interfaccia funzionale (ovvero un'interfaccia con un singolo metodo) che accetta e restituisce esattamente ciò che si desidera.

Per questo Java 8 viene fornito con un set di tipi di interfaccia comunemente utilizzati in java.util.function (grazie a Maurice Naftalin per l'hint su JavaDoc).

Per questo specifico caso d'uso c'è java.util.function.IntBinaryOperator con a single int applyAsInt(int left, int right) method, così si potrebbe scrivere il vostro method come questo:

static int method(IntBinaryOperator op){ 
    return op.applyAsInt(5, 10); 
} 

Ma si può altrettanto bene definire la propria interfaccia e usarlo in questo modo:

public interface TwoArgIntOperator { 
    public int op(int a, int b); 
} 

//elsewhere: 
static int method(TwoArgIntOperator operator) { 
    return operator.op(5, 10); 
} 

L'utilizzo della propria interfaccia presenta il vantaggio che è possibile avere nomi che indicano più chiaramente l'intento.

+2

Ci saranno interfacce integrate da usare, o devo creare un'interfaccia per ogni lambda che voglio prendere? – Marius

+0

Un buon compromesso rispetto al dilemma del nome descrittivo e della riusabilità sarebbe quello di estendere l'interfaccia incorporata senza sovrascrivere il metodo che specifica. Questo ti dà il tuo nome descrittivo con solo una singola riga di codice aggiuntiva. –

+0

Non capisco. Può passare lambda per qualcosa e funzionerà? Cosa succede se passa '(int a, int b, int c)' per 'TwoArgIntOperator'. Cosa succede se 'TwoArgIntOperator' ha ** due ** metodi con la stessa firma. Questa risposta è confusa. –

12

Esiste una versione pubblica accessibile dal Web dei JavaDocs Java 8 abilitati per Lambda, collegati da http://lambdafaq.org/lambda-resources. (Questo dovrebbe ovviamente essere un commento sulla risposta di Joachim Sauer, ma non posso entrare nel mio account SO con i punti di reputazione che ho bisogno di aggiungere un commento.) Il sito lambdafaq (lo mantengo) risponde a questo ea molti altri Java -lambda domande.

+2

Grazie per il link e il fatto che tu mantenga quel sito! Mi sono preso la libertà di aggiungere collegamenti al tuo JavaDoc pubblico alla mia risposta. –

+1

Come nota a margine: sembra che tu stia costruendo per Lambdas ciò che Angelika Langer ha [costruito per Generics] (http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html). Grazie per questo, Java ha bisogno di tali risorse! –

39

Per utilizzare l'espressione Lambda è necessario creare la propria interfaccia funzionale o utilizzare l'interfaccia funzionale Java per operazioni che richiedono due numeri interi e restituiscono come valore. IntBinaryOperator

Uso utente definito interfaccia funzionale

interface TwoArgInterface { 

    public int operation(int a, int b); 
} 

public class MyClass { 

    public static void main(String javalatte[]) { 
     // this is lambda expression 
     TwoArgInterface plusOperation = (a, b) -> a + b; 
     System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34)); 

    } 
} 

Utilizzando interfaccia funzionale Java

import java.util.function.IntBinaryOperator; 

public class MyClass1 { 

    static void main(String javalatte[]) { 
     // this is lambda expression 
     IntBinaryOperator plusOperation = (a, b) -> a + b; 
     System.out.println("Sum of 10,34 : " + plusOperation.applyAsInt(10, 34)); 

    } 
} 

Altro esempio ho creato è here

+0

Il collegamento alla documentazione 'IntBinaryOperator' è morto. – Hendrikto

+1

questo è il miglior esempio di lambda che ho trovato finora, ed è stato l'unico che mi ha davvero "capito" finalmente. – JimmySmithJR

22

Per funzioni tha Non ho più di 2 parametri, puoi passarli senza definire la tua interfaccia. Ad esempio,

class Klass { 
    static List<String> foo(Integer a, String b) { ... } 
} 

class MyClass{ 

    static List<String> method(BiFunction<Integer, String, List<String>> fn){ 
    return fn.apply(5, "FooBar"); 
    } 
} 

List<String> lStr = MyClass.method((a, b) -> Klass.foo((Integer) a, (String) b)); 

In BiFunction<Integer, String, List<String>>, Integer e String sono relativi parametri, e List<String> è il suo tipo di ritorno.

Per una funzione con un solo parametro, è possibile utilizzare Function<T, R>, dove T è il suo tipo di parametro, e R è il suo tipo di valore di ritorno. Fare riferimento a questo page per tutte le interfacce che sono già rese disponibili da Java.

3

L'espressione lambda può essere passata come argomento. Per passare un'espressione lambda come argomento, il tipo del parametro (che riceve l'espressione lambda come argomento) deve essere di tipo interfaccia funzionale.

Se c'è un'interfaccia funzionale -

interface IMyFunc { 
    boolean test(int num); 
} 

E c'è un metodo del filtro che aggiunge l'int nella lista solo se è maggiore di 5. Nota qui che il metodo del filtro ha un'interfaccia funtional IMyFunc come uno del parametro. In quel caso l'espressione lambda può essere passata come argomento per il parametro method.

public class LambdaDemo { 
    public static List<Integer> filter(IMyFunc testNum, List<Integer> listItems) { 
     List<Integer> result = new ArrayList<Integer>(); 
     for(Integer item: listItems) { 
      if(testNum.test(item)) { 
       result.add(item); 
      } 
     } 
     return result; 
    } 
    public static void main(String[] args) { 
     List<Integer> myList = new ArrayList<Integer>(); 
     myList.add(1); 
     myList.add(4); 
     myList.add(6); 
     myList.add(7); 
     // calling filter method with a lambda expression 
     // as one of the param 
     Collection<Integer> values = filter(n -> n > 5, myList); 

     System.out.println("Filtered values " + values); 
    } 
} 
-3

C'è flessibilità nell'uso di lambda come parametro. Abilita la programmazione funzionale in java. La sintassi di base è

param -> method_body

seguito è un modo, è possibile definire un metodo di prendere interfaccia funzionale (lambda è usato) come parametro. a. se si desidera definire un metodo dichiarato all'interno di un'interfaccia funzionale, dicono, il interfaccia funzionale viene dato come argomento/parametro a un metodo chiamato da main()

@FunctionalInterface 
interface FInterface{ 
    int callMeLambda(String temp); 
} 


class ConcreteClass{ 

    void funcUsesAnonymousOrLambda(FInterface fi){ 
     System.out.println("===Executing method arg instantiated with Lambda===")); 
    } 

    public static void main(){ 
     // calls a method having FInterface as an argument. 
     funcUsesAnonymousOrLambda(new FInterface() { 

      int callMeLambda(String temp){ //define callMeLambda(){} here.. 
       return 0; 
      } 
     } 
    } 

/***********Can be replaced by Lambda below*********/ 
     funcUsesAnonymousOrLambda((x) -> { 
      return 0; //(1) 
     } 

    } 

FInterface fi = (x) - > {return 0; };

funcUsesAnonymousOrLambda (fi);

Qui sopra si può vedere come è possibile sostituire un'espressione lambda con un'interfaccia.

Sopra spiega un particolare utilizzo di espressione lambda, ce ne sono altri. rif. Java 8 lambda within a lambda can't modify variable from outer lambda