Comment définir une méthode qui prend un lambda comme paramètre dans Java 8?

363

Dans Java 8, les méthodes peuvent être créées sous forme d'expressions Lambda et peuvent être transmises par référence (avec un peu de travail sous le capot). Il existe de nombreux exemples en ligne avec des lambdas créés et utilisés avec des méthodes, mais aucun exemple sur la façon de créer une méthode prenant un lambda comme paramètre. Quelle est la syntaxe pour cela?

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


class MyClass{
  //How do I define this method?
  static int method(Lambda l){
    return l(5, 10);
  }
}
Marius
la source
29
Bonne question. Et vous avez raison: aucun des didacticiels ne contient cette partie.
Martin

Réponses:

247

Les lambdas sont purement une construction de site d'appel: le destinataire du lambda n'a pas besoin de savoir qu'un lambda est impliqué, mais accepte une interface avec la méthode appropriée.

En d'autres termes, vous définissez ou utilisez une interface fonctionnelle (c'est-à-dire une interface avec une seule méthode) qui accepte et renvoie exactement ce que vous voulez.

Pour cela, Java 8 est livré avec un ensemble de types d'interfaces couramment utilisés dans java.util.function(merci à Maurice Naftalin pour l'astuce sur le JavaDoc).

Pour ce cas d'utilisation spécifique , il y a java.util.function.IntBinaryOperatoravec une seule int applyAsInt(int left, int right)méthode , vous pouvez écrire votre methodcomme ceci:

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

Mais vous pouvez tout aussi bien définir votre propre interface et l'utiliser comme ceci:

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

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

L'utilisation de votre propre interface présente l'avantage que vous pouvez avoir des noms qui indiquent plus clairement l'intention.

Joachim Sauer
la source
5
Y aura-t-il des interfaces intégrées à utiliser, ou dois-je créer une interface pour chaque lambda que je veux prendre?
Marius
Un bon compromis au dilemme de la réutilisabilité par rapport au nom descriptif serait d'étendre l'interface intégrée sans remplacer la méthode qu'il spécifie. Cela vous donne votre nom descriptif avec une seule ligne de code supplémentaire.
Will Byrne
Je ne comprends pas. Il peut passer lambda pour rien et ça marchera? Qu'arrive - t - il se passe (int a, int b, int c)pour TwoArgIntOperator. Que se passe-t-il si TwoArgIntOperatora deux méthodes avec la même signature. Cette réponse prête à confusion.
Tomáš Zato - Rétablir Monica le
7
@ TomášZato: si vous utilisez un lambda avec des arguments non correspondants, le compilateur se plaindra. Et les interfaces avec deux méthodes (non par défaut) ne seront pas utilisables comme lambdas, car seules les interfaces fonctionnelles peuvent être utilisées.
Joachim Sauer
Une question de base: je pense que je ne comprends toujours pas l'aspect de passer une méthode en tant que paramètre avec les paramètres de cette méthode manquants. S'il passe TwoArgIntOperator en tant que paramètre et qu'il doit passer le paramètre de cette méthode séparément, cela ne semble-t-il pas laid? Existe-t-il un moyen de transmettre le corps d'exécution complet avec le paramètre? Comme dans votre exemple, un moyen d'éviter de coder en dur "5" et "10".
instanceOfObject
63

Pour utiliser l'expression Lambda, vous devez soit créer votre propre interface fonctionnelle, soit utiliser l'interface fonctionnelle Java pour les opérations qui nécessitent deux entiers et renvoyer comme valeur. IntBinaryOperator

Utilisation d'une interface fonctionnelle définie par l'utilisateur

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

    }
}

Utilisation de l'interface fonctionnelle 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));

    }
}

Un autre exemple que j'ai créé est ici

pardeep131085
la source
Le lien vers la IntBinaryOperatordocumentation est mort.
Hendrikto
1
c'est le meilleur exemple de lambda que j'ai trouvé jusqu'à présent, et c'est le seul qui m'a vraiment fait «l'obtenir» finalement.
JimmySmithJR
1
Sooooooo ... essentiellement un délégué, mais nous ne sommes pas censés l'appeler ainsi?
Ryan Lundy
37

Pour les fonctions qui n'ont pas plus de 2 paramètres, vous pouvez les passer sans définir votre propre interface. Par exemple,

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

Dans BiFunction<Integer, String, List<String>>, Integeret Stringsont ses paramètres, et List<String>est son type de retour.

Pour une fonction avec un seul paramètre, vous pouvez utiliser Function<T, R>, où Test son type de paramètre et Rson type de valeur de retour. Reportez-vous à cette page pour toutes les interfaces déjà mises à disposition par Java.

David Wu
la source
15

Il existe une version publique accessible sur le Web des JavaDocs Java 8 compatibles Lambda, liée depuis http://lambdafaq.org/lambda-resources . (Cela devrait évidemment être un commentaire sur la réponse de Joachim Sauer, mais je ne peux pas accéder à mon compte SO avec les points de réputation dont j'ai besoin pour ajouter un commentaire.) Le site lambdafaq (je le maintiens) répond à cela et à beaucoup d'autres Java -lambda questions.

NB Cette réponse a été écrite avant que la documentation Java 8 GA ne soit rendue publique . J'ai laissé en place, cependant, car la FAQ Lambda pourrait toujours être utile aux personnes qui découvrent les fonctionnalités introduites dans Java 8.

Maurice Naftalin
la source
2
Merci pour le lien et le fait que vous mainteniez ce site! J'ai pris la liberté d'ajouter des liens vers votre JavaDoc public à ma réponse.
Joachim Sauer
1
En remarque: il semble que vous construisez pour Lambdas ce qu'Angelika Langer a construit pour Generics . Merci pour cela, Java a besoin de telles ressources!
Joachim Sauer
1
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change. - De l'avis
ClearLogic
@ClearLogic Oui, d'accord. AFAIR Je ne voulais rien ajouter aux réponses existantes, mais seulement pour indiquer où j'avais posté une copie de la documentation de l'API, qui à l'époque n'était pas facilement accessible.
Maurice Naftalin
7

Pour moi, la solution la plus logique est de définir une Callbackinterface:

interface Callback {
    void call();
}

puis de l'utiliser comme paramètre dans la fonction que vous souhaitez appeler:

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

Mais juste une précision

Un lambda n'est pas une interface spéciale, une classe ou tout autre élément que vous pouvez déclarer vous-même. Lambdaest juste le nom donné à la () -> {}syntaxe spéciale, qui permet une meilleure lisibilité lors du passage d'interfaces mono-méthode en paramètre. Il a été conçu pour remplacer ceci:

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

Donc, dans l'exemple ci-dessus, ce Callbackn'est pas un lambda, c'est juste une interface régulière; lambdaest le nom de la syntaxe de raccourci que vous pouvez utiliser pour l'implémenter.

flawyte
la source
6

Pour quiconque cherche sur Google, une bonne méthode serait d'utiliser 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'outprint serait: 166

Big_Bad_E
la source
1
Au lieu de Consumer<Pair<A,B>>, utilisez BiConsumer<A,B>pour ce cas. ( docs )
nobar
Je ne savais pas que cela existait, je devrais passer en revue le package de fonctions la prochaine fois.
Big_Bad_E
5

L'expression lambda peut être passée comme argument. Pour passer une expression lambda comme argument, le type du paramètre (qui reçoit l'expression lambda comme argument) doit être de type interface fonctionnelle.

S'il y a une interface fonctionnelle -

interface IMyFunc {
   boolean test(int num);
}

Et il existe une méthode de filtrage qui ajoute l'int dans la liste uniquement s'il est supérieur à 5. Notez ici que la méthode de filtrage a l'interface fonctionnelle IMyFunc comme l'un des paramètres. Dans ce cas, l'expression lambda peut être passée comme argument pour le paramètre de méthode.

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);
    }
}
infoj
la source
2

Vous pouvez utiliser des interfaces fonctionnelles comme mentionné ci-dessus. voici quelques exemples

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

J'espère que cela aide

ank
la source
1

Eh bien, c'est facile. Le but de l'expression lambda est d'implémenter l'interface fonctionnelle. C'est l'interface avec une seule méthode. Voici un article génial sur les interfaces fonctionnelles prédéfinies et héritées.

Quoi qu'il en soit, si vous souhaitez implémenter votre propre interface fonctionnelle, faites-le. Juste pour un exemple simple:

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

Faisons donc une classe, où nous allons créer une méthode, qui accepte le type de MyFunctionalInterface :

public class Main {

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

    public static void main(String[] args) {

    }
}

La dernière chose à faire est de passer l'implémentation de MyFunctionalInterface à la méthode que nous avons définie:

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

C'est ça!

SanchelliosProg
la source
1

Lambda n'est pas un objet mais une interface fonctionnelle. On peut définir autant d'interfaces fonctionnelles que possible en utilisant @FuntionalInterface comme annotation

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

La sortie sera la suivante

Output : 7

Le concept de base d'une expression Lambda consiste à définir votre propre logique mais des arguments déjà définis. Ainsi, dans le code ci-dessus, vous pouvez modifier la définition de la fonction do en plus de toute autre définition, mais vos arguments sont limités à 2.

raja emani
la source
1

Fondamentalement, pour passer une expression lamda comme paramètre, nous avons besoin d'un type dans lequel nous pouvons la contenir. Tout comme une valeur entière que nous détenons dans la classe prim ou int primitive . Java n'a pas de type distinct pour l'expression lamda à la place, il utilise une interface comme type pour contenir l'argument. Mais cette interface devrait être une interface fonctionnelle .

Vamshi
la source
0

Faites ce qui suit ..

Vous avez déclaré method(lambda l) Tout ce que vous voulez faire, c'est créer une interface avec le nom lambdaet déclarer une méthode abstraite

public int add(int a,int b);  

le nom de la méthode n'a pas d'importance ici ..

Ainsi, lorsque vous appelez MyClass.method( (a,b)->a+b) cette implémentation (a,b)->a+bsera injectée dans votre méthode d'ajout d'interface. Donc, chaque fois que vous appelez, l.addelle prendra cette implémentation et effectuera l'ajout de aet b et return l.add(2,3)reviendra 5. - Fondamentalement, c'est ce que fait lambda ..

Arasn
la source
-1

Voici à peu près comment C # gère ce problème (mais exprimé en code Java). Quelque chose comme ça pourrait répondre à presque tous vos besoins:

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);
    }
}
craigrs84
la source
-2

Il y a une flexibilité dans l'utilisation de lambda comme paramètre. Il permet une programmation fonctionnelle en java. La syntaxe de base est

param -> corps_méthode

Voici un moyen, vous pouvez définir une méthode prenant l'interface fonctionnelle (lambda est utilisé) comme paramètre. une. si vous souhaitez définir une méthode déclarée à l'intérieur d'une interface fonctionnelle, disons, l' interface fonctionnelle est donnée comme argument / paramètre à une méthode appelée depuismain()

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

Ci-dessus, on peut voir comment une expression lambda peut être remplacée par une interface.

Ci-dessus explique une utilisation particulière de l'expression lambda, il y en a plus. ref Java 8 lambda dans un lambda ne peut pas modifier la variable de lambda externe

hi.nitish
la source