Come definisco un metodo che accetta un lambda come parametro in Java 8?


363

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 molti esempi online con lambda creati e usati con metodi, ma nessun esempio su come fare un metodo prendendo un lambda come parametro. Qual è la sintassi per quello?

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


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

29
Buona domanda. E hai ragione: nessuno dei tutorial contiene quella parte.
Martin,

Risposte:


247

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

In altre parole, definisci o usi un'interfaccia funzionale (cioè un'interfaccia con un singolo metodo) che accetta e restituisce esattamente ciò che desideri.

Per questo Java 8 viene fornito con un set di tipi di interfaccia comunemente usati in java.util.function(grazie a Maurice Naftalin per il suggerimento su JavaDoc).

Per questo caso specifico impiego ci sia java.util.function.IntBinaryOperatorcon un unico int applyAsInt(int left, int right)metodo , così si potrebbe scrivere il vostro methodin questo modo:

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

Ma puoi anche definire la tua interfaccia e usarla 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'uso della propria interfaccia ha il vantaggio di poter avere nomi che indicano più chiaramente l'intento.


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

Un buon compromesso alla riusabilità rispetto al dilemma del nome descrittivo sarebbe quello di estendere l'interfaccia incorporata senza sovrascrivere il metodo specificato. Questo ti dà il tuo nome descrittivo con una sola riga di codice aggiuntiva.
Will Byrne,

Non capisco Può passare lambda per qualsiasi cosa e funzionerà? Cosa succede se passa (int a, int b, int c)per TwoArgIntOperator. Cosa succede se TwoArgIntOperatorha due metodi con la stessa firma. Questa risposta è confusa.
Tomáš Zato - Ripristina Monica il

7
@ TomášZato: se usi un lambda con argomenti non corrispondenti il ​​compilatore si lamenterà. E le interfacce con due metodi (non predefiniti) non saranno utilizzabili come lambda, poiché possono essere utilizzate solo interfacce funzionali .
Joachim Sauer il

Una domanda di base: penso di non capire ancora l'aspetto del passaggio di un metodo come parametro con i parametri di quel metodo mancanti. Se sta passando TwoArgIntOperator come parametro e deve passare il parametro di quel metodo separatamente, non sembra brutto? C'è un modo per passare l'intero corpo di esecuzione insieme al parametro? Come nel tuo esempio, un modo per evitare la codifica "5" e "10".
instanceOfObject,

63

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

Utilizzo dell'interfaccia funzionale definita dall'utente

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

    }
}

Utilizzo dell'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 che ho creato è qui


Il link alla IntBinaryOperatordocumentazione è morto.
Hendrikto,

1
questo è il miglior esempio di lambda che ho trovato finora, ed è stato l'unico che mi ha fatto davvero "capire" alla fine.
JimmySmithJR l'

1
Sooooooo ... fondamentalmente un delegato, ma non dovremmo chiamarlo così?
Ryan Lundy,

37

Per le funzioni che non hanno più di 2 parametri, è possibile passarli senza definire la propria interfaccia. Per 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>>, Integere Stringsono i suoi parametri, ed List<String>è il suo tipo di ritorno.

Per una funzione con un solo parametro, è possibile utilizzare Function<T, R>, dove si Ttrova il tipo di parametro e Ril tipo di valore restituito. Fare riferimento a questa pagina per tutte le interfacce che sono già rese disponibili da Java.


15

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 riesco ad accedere al mio account SO con i punti reputazione di cui ho bisogno per aggiungere un commento.) Il sito lambdafaq (lo sostengo) risponde a questo e molti altri Java -lambda domande.

NB Questa risposta è stata scritta prima che la documentazione di Java 8 GA diventasse pubblicamente disponibile . Ho lasciato il posto, però, perché le FAQ Lambda potrebbero essere ancora utili per le persone che imparano le funzionalità introdotte in Java 8.


2
Grazie per il link e per il fatto di mantenere quel sito! Mi sono preso la libertà di aggiungere collegamenti al tuo JavaDoc pubblico alla mia risposta.
Joachim Sauer,

1
Come nota a margine: sembra che tu stia costruendo per Lambdas quello che Angelika Langer ha realizzato per Generics . Grazie per questo, Java ha bisogno di tali risorse!
Joachim Sauer,

1
Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. - Dalla recensione
ClearLogic,

@ClearLogic Sì, d'accordo. AFAIR Non volevo aggiungere nulla alle risposte esistenti, ma solo per sottolineare dove avevo pubblicato una copia della documentazione dell'API, che a quel tempo non era altrimenti facilmente accessibile.
Maurice Naftalin,

7

Per me, la soluzione che ha più senso è definire Callbackun'interfaccia:

interface Callback {
    void call();
}

e quindi per usarlo come parametro nella funzione che si desidera chiamare:

void somewhereInYourCode() {
    method(() -> {
        // You've passed a lambda!
        // method() is done, do whatever you want here.
    });
}

void method(Callback callback) {
    // Do what you have to do
    // ...

    // Don't forget to notify the caller once you're done
    callback.call();
}

Solo una precisione però

Una lambda non è un'interfaccia speciale, una classe o qualsiasi altra cosa che potresti dichiarare da solo. Lambdaè solo il nome assegnato alla () -> {}sintassi speciale, che consente una migliore leggibilità quando si passano interfacce a metodo singolo come parametro. È stato progettato per sostituire questo:

method(new Callback() {
    @Override
    public void call() {
        // Classic interface implementation, lot of useless boilerplate code.
        // method() is done, do whatever you want here.
    }
});

Quindi nell'esempio sopra, nonCallback è un lambda, è solo un'interfaccia regolare; è il nome della sintassi del collegamento che è possibile utilizzare per implementarlo.lambda


6

Per chiunque stia cercando su Google questo, un buon metodo sarebbe usare java.util.function.BiConsumer. ex:

Import java.util.function.Consumer
public Class Main {
    public static void runLambda(BiConsumer<Integer, Integer> lambda) {
        lambda.accept(102, 54)
    }

    public static void main(String[] args) {
        runLambda((int1, int2) -> System.out.println(int1 + " + " + int2 + " = " + (int1 + int2)));
    }

L'impronta sarebbe: 166


1
Invece di Consumer<Pair<A,B>>, utilizzare BiConsumer<A,B>per questo caso. ( documenti )
nobar,

Non sapevo che esistesse, dovrei setacciare il pacchetto di funzioni la prossima volta.
Big_Bad_E

5

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

Se esiste un'interfaccia funzionale -

interface IMyFunc {
   boolean test(int num);
}

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

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

2

È possibile utilizzare le interfacce funzionali come indicato sopra. di seguito sono riportati alcuni esempi

Function<Integer, Integer> f1 = num->(num*2+1);
System.out.println(f1.apply(10));

Predicate<Integer> f2= num->(num > 10);
System.out.println(f2.test(10));
System.out.println(f2.test(11));

Supplier<Integer> f3= ()-> 100;
System.out.println(f3.get());

Spero che sia d'aiuto


1

Bene, è facile. Lo scopo dell'espressione lambda è implementare l'interfaccia funzionale. È l'interfaccia con un solo metodo. Ecco un fantastico articolo sulle interfacce funzionali predefinite e legacy.

Ad ogni modo, se vuoi implementare la tua interfaccia funzionale, fallo. Solo per un semplice esempio:

public interface MyFunctionalInterface {
    String makeIt(String s);
}

Quindi facciamo una classe, dove creeremo un metodo, che accetta il tipo di MyFunctionalInterface :

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {

    }
}

L'ultima cosa che dovresti fare è passare l'implementazione di MyFunctionalInterface al metodo che abbiamo definito:

public class Main {

    static void printIt(String s, MyFunctionalInterface f) {
        System.out.println(f.makeIt(s));
    }

    public static void main(String[] args) {
        printIt("Java", s -> s + " is Awesome");
    }
}

Questo è tutto!


1

Lambda non è un oggetto ma un'interfaccia funzionale. Si possono definire quante più interfacce funzionali possono usare @FuntionalInterface come annotazione

@FuntionalInterface
public interface SumLambdaExpression {
     public int do(int a, int b);
}

public class MyClass {
     public static void main(String [] args) {
          SumLambdaExpression s = (a,b)->a+b;
          lambdaArgFunction(s);
     }

     public static void lambdaArgFunction(SumLambdaExpression s) {
          System.out.println("Output : "+s.do(2,5));
     }
}

L'output sarà il seguente

Output : 7

Il concetto di base di un'espressione Lambda è definire la propria logica ma argomenti già definiti. Quindi nel codice sopra puoi cambiare la definizione della funzione do dall'aggiunta a qualsiasi altra definizione, ma i tuoi argomenti sono limitati a 2.


1

Fondamentalmente per passare un'espressione lamda come parametro, abbiamo bisogno di un tipo in cui possiamo tenerlo. Proprio come un valore intero che teniamo nella primitiva int o nella classe Integer. Java non ha un tipo separato per l'espressione lamda, ma usa un'interfaccia come tipo per contenere l'argomento. Ma quell'interfaccia dovrebbe essere un'interfaccia funzionale .


0

Fare quanto segue ..

Hai dichiarato method(lambda l) Tutto quello che vuoi fare è creare un'interfaccia con il nome lambdae dichiarare un metodo astratto

public int add(int a,int b);  

il nome del metodo non ha importanza qui.

Così, quando u chiamata MyClass.method( (a,b)->a+b) Questa applicazione (a,b)->a+bverrà iniettato al tuo metodo di interfaccia add .Così ogni volta che si chiama l.addsi sta andando a prendere questa implementazione ed eseguire l'aggiunta di ae b e return l.add(2,3)tornerà 5. - Fondamentalmente questo è ciò che Lambda fa ..


-1

Ecco come C # gestisce questo problema (ma espresso come codice Java). Qualcosa del genere potrebbe gestire quasi tutte le tue esigenze:

import static org.util.function.Functions.*;

public class Test {

    public static void main(String[] args)
    {
        Test.invoke((a, b) -> a + b);       
    }

    public static void invoke(Func2<Integer, Integer, Integer> func)
    {
        System.out.println(func.apply(5, 6));
    }
}

package org.util.function;

public interface Functions {

    //Actions:
    public interface Action {
        public void apply();
    }

    public interface Action1<T1> {
        public void apply(T1 arg1);
    }

    public interface Action2<T1, T2> {
        public void apply(T1 arg1, T2 arg2);
    }

    public interface Action3<T1, T2, T3> {
        public void apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Action4<T1, T2, T3, T4> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Action5<T1, T2, T3, T4, T5> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Action6<T1, T2, T3, T4, T5, T6> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Action7<T1, T2, T3, T4, T5, T6, T7> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Action8<T1, T2, T3, T4, T5, T6, T7, T8> {
        public void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }

    //Functions:
    public interface Func<TResult> {
        public TResult apply();
    }

    public interface Func1<T1, TResult> {
        public TResult apply(T1 arg1);
    }

    public interface Func2<T1, T2, TResult> {
        public TResult apply(T1 arg1, T2 arg2);
    }

    public interface Func3<T1, T2, T3, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3);
    }

    public interface Func4<T1, T2, T3, T4, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
    }

    public interface Func5<T1, T2, T3, T4, T5, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
    }

    public interface Func6<T1, T2, T3, T4, T5, T6, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
    }

    public interface Func7<T1, T2, T3, T4, T5, T6, T7, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
    }

    public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, TResult> {
        public TResult apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
    }
}

-2

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

param -> method_body

Di seguito è riportato un modo, è possibile definire un metodo prendendo l'interfaccia funzionale (viene utilizzata lambda) come parametro. un. se si desidera definire un metodo dichiarato all'interno di un'interfaccia funzionale, ad esempio, l' interfaccia funzionale viene fornita come argomento / parametro a un metodo chiamato damain()

@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 un'espressione lambda può essere sostituita con un'interfaccia.

Sopra spiega un uso particolare dell'espressione lambda, ce ne sono altri. ref Java 8 lambda all'interno di un lambda non può modificare la variabile dal lambda esterno

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.