Comment passer une fonction en paramètre dans Java? [dupliquer]

Cette question a déjà une réponse ici:

Est-il possible de passer une méthode dans une méthode Java en tant que paramètre? Si oui, quelqu'un pourrait-il svp me guider? Cela ne semble pas trivial

335
demandé sur Jason 2011-01-14 00:37:32

8 réponses

Java 8 et supérieur

En utilisant des expressions Java 8+ lambda, si vous avez une classe ou une interface avec une seule méthode, par exemple:

public interface MyInterface {
    String doSomething(int param1, String param2);
}

Ensuite, partout où MyInterface est utilisé, vous pouvez remplacer une expression lambda:

class MyClass {
    public MyInterface myInterface = (p1, p2) -> { return p2 + p1; };
}

Par exemple, vous pouvez créer un nouveau thread très rapidement:

new Thread(() -> someMethod()).start();

Et utilisez la syntaxe de référence de la méthode pour la rendre encore plus propre:

new Thread(this::someMethod).start();

Sans les expressions lambda, ces deux derniers exemples ressemblerait à:

new Thread(new Runnable() { someMethod(); }).start();

Avant Java 8

Un modèle commun serait de l'envelopper dans une interface, comme Callable, par exemple, puis vous passez un appelable:

public T myMethod(Callable<T> func) {
    return func.call();
}

Ce modèle est connu comme le modèle de commande .

Gardez à l'esprit que vous feriez mieux de créer une interface pour votre utilisation particulière. Si vous choisissez d'aller avec callable, vous remplacerez T ci-dessus par n'importe quel type de valeur de retour que vous attendez, comme String.

Dans réponse à votre commentaire ci-dessous, vous pouvez dire:

public int methodToPass() { 
        // do something
}

public void dansMethod(int i, Callable<Integer> myFunc) {
       // do something
}

Alors appelez-le, peut-être en utilisant une classe interne anonyme:

dansMethod(100, new Callable<Integer>() {
   public Integer call() {
        return methodToPass();
   }
});

Gardez à l'esprit que ce n'est pas un "truc". C'est juste l'équivalent conceptuel de base de java pour les pointeurs de fonction.

371
répondu jk. 2018-08-25 16:52:05

Vous pouvez utiliser Java reflection pour ce faire. La méthode serait représentée comme une instance de java.lang.refléter.Méthode .

import java.lang.reflect.Method;

public class Demo {

    public static void main(String[] args) throws Exception{
        Class[] parameterTypes = new Class[1];
        parameterTypes[0] = String.class;
        Method method1 = Demo.class.getMethod("method1", parameterTypes);

        Demo demo = new Demo();
        demo.method2(demo, method1, "Hello World");
    }

    public void method1(String message) {
        System.out.println(message);
    }

    public void method2(Object object, Method method, String message) throws Exception {
        Object[] parameters = new Object[1];
        parameters[0] = message;
        method.invoke(object, parameters);
    }

}
114
répondu Blaise Doughan 2017-04-12 20:01:05

Expressions Lambda

Pour ajouter à jk.excellente réponse , Vous pouvez maintenant passer une méthode plus facilement en utilisant Expressions Lambda (en Java 8). Tout d'abord, quelques antécédents. Une interface fonctionnelle est une interface qui a une et une seule méthode abstraite, bien qu'elle puisse contenir n'importe quel nombre de méthodes par défaut (nouveau dans Java 8) et méthodes statiques. Une expression lambda peut rapidement implémenter la méthode abstraite, sans tous les syntaxe inutile nécessaire si vous n'utilisez pas une expression lambda.

Sans expressions lambda:

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});

Avec des expressions lambda:

obj.aMethod(i -> i == 982);

Voici un extrait de le tutoriel Java sur les Expressions Lambda :

Syntaxe des Expressions Lambda

Une expression lambda se compose des éléments suivants:

  • Une liste de paramètres formels séparés par des virgules entre parenthèses. Le CheckPerson.méthode d'essai contient un paramètre, p, qui représente une instance de la classe Person.

    Note: Vous peut omettre le type de données des paramètres dans une expression lambda. Dans en outre, vous pouvez omettre les parenthèses s'il n'y a qu'un seul paramètre. Par exemple, l'expression lambda suivante est également valide:

    p -> p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
    
  • Le jeton de flèche, ->

  • Un corps, qui consiste en une seule expression ou un bloc d'instruction. Cet exemple utilise le expression suivante:

    p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
    

    Si vous spécifiez une seule expression, le runtime Java évalue l'expression et renvoie sa valeur. Alternativement, vous pouvez utiliser une déclaration de retour:

    p -> {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }
    

    Une instruction return n'est pas une expression; dans une expression lambda, vous devez placer des instructions entre accolades ({}). Cependant, vous n'avez pas pour placer une invocation de méthode void entre accolades. Par exemple, l' voici une expression lambda valide:

    email -> System.out.println(email)
    

Notez qu'une expression lambda ressemble beaucoup à une déclaration de méthode; vous pouvez considérer les expressions lambda anonyme méthodes sans nom.


Voici comment vous pouvez "passer une méthode" en utilisant une expression lambda:

remarque: cela utilise une nouvelle interface fonctionnelle standard, java.util.function.IntConsumer.

class A {
    public static void methodToPass(int i) { 
        // do stuff
    }
}
import java.util.function.IntConsumer;

class B {
    public void dansMethod(int i, IntConsumer aMethod) {
        /* you can now call the passed method by saying aMethod.accept(i), and it
        will be the equivalent of saying A.methodToPass(i) */
    }
}
class C {
    B b = new B();

    public C() {
        b.dansMethod(100, j -> A.methodToPass(j));   //Lambda Expression here
    }
}

L'exemple ci-dessus peut être raccourci encore plus en utilisant le :: opérateur.

public C() {
    b.dansMethod(100, A::methodToPass);
}
82
répondu The Guy with The Hat 2017-05-23 10:31:37

Grâce à Java 8, Vous n'avez pas besoin de faire les étapes ci-dessous pour passer une fonction à une méthode, c'est ce que sont les lambdas, voir tutoriel D'Expression Lambda D'Oracle. Le reste de ce post décrit ce que nous avions à faire dans le mauvais vieux temps afin d'implémenter cette fonctionnalité.

Généralement, vous déclarez votre méthode comme prenant une interface avec une seule méthode, puis vous passez un objet qui implémente cette interface. Un exemple est dans commons-collections, où vous avez interfaces pour la fermeture, le transformateur et le prédicat, et méthodes dans lesquelles vous transmettez des implémentations de ceux-ci. La goyave est la nouvelle amélioration de commons-collections, vous pouvez y trouver des interfaces équivalentes.

Par exemple, commons-collections a org.Apache.commun.collection.CollectionUtils, qui a beaucoup de méthodes statiques qui prennent des objets passés, pour en choisir un au hasard, il y en a un appelé exists avec cette signature:

static boolean exists(java.util.Collection collection, Predicate predicate) 

, Il prend un objet qui implémente l'interface Prédicat, ce qui signifie qu'il doit avoir une méthode qui prend un Objet et retourne un booléen.

, Donc je peux l'appeler comme ceci:

CollectionUtils.exists(someCollection, new Predicate() {
    public boolean evaluate(Object object) { 
        return ("a".equals(object.toString());
    }
});

Et il renvoie true ou false selon que someCollection contient un objet pour lequel le prédicat renvoie true.

Quoi qu'il en soit, ce n'est qu'un exemple, et commons-collections est obsolète. J'oublie juste l'équivalent en Goyave.

14
répondu Nathan Hughes 2016-01-20 16:02:15

Java supporte très bien les fermetures. Il ne supporte tout simplement pas les fonctions , donc la syntaxe à laquelle vous êtes habitué pour les fermetures est beaucoup plus gênante et volumineuse: vous devez tout emballer dans une classe avec une méthode. Par exemple,

public Runnable foo(final int x) {
  return new Runnable() {
    public void run() {
      System.out.println(x);
    }
  };
}

Retournera un objet Runnable dont la méthode run() "ferme" le x transmis, comme dans n'importe quel langage qui prend en charge les fonctions et les fermetures de première classe.

5
répondu amalloy 2012-10-01 18:06:59

J'ai utilisé le modèle de commande que @jk. mentionné, en ajoutant un type de retour:

public interface Callable<I, O> {

    public O call(I input);

}
4
répondu Aram Kocharyan 2012-09-30 14:02:46

Je sais que c'est un post plutôt ancien mais j'ai une autre solution un peu plus simple. Vous pouvez créer une autre classe à l'intérieur et la rendre abstraite. Ensuite, faites une méthode abstraite nommez-la comme vous voulez. Dans la classe d'origine, créez une méthode qui prend la nouvelle classe comme paramètre, dans cette méthode, appelez la méthode abstraite. Il ressemblera à quelque chose comme ça.

public class Demo {

public Demo(/.../){

}


public void view(Action a){
    a.preform();
}


/**
 * The Action Class is for making the Demo
 * View Custom Code
 */
public abstract class Action {

    public Action(/.../){

    }

    abstract void preform();
}
}

Maintenant, vous pouvez faire quelque chose comme ceci pour appeler une méthode à partir de la classe.

/...
Demo d = new Demo;
Action a = new Action() {

    @Override
    void preform() {
        //Custom Method Code Goes Here
    }
};

/.../
d.view(a)

Comme je l'ai dit, je sais que c'est vieux mais de cette façon, je pense est un peu plus facile. Espérons que cela aide.

4
répondu Zatran 2013-08-07 17:23:59

Java ne prend pas (encore) en charge les fermetures. Mais il existe d'autres langages comme Scala et Groovy qui s'exécutent dans la JVM et prennent en charge les fermetures.

-8
répondu Claude 2011-01-13 21:43:15