web-dev-qa-db-fra.com

Pointeurs de fonction dans Java

Cela peut sembler banal et trivial, mais il me semble avoir du mal à trouver une réponse concrète. En C #, il existe un concept de délégué, qui est étroitement lié à l'idée de pointeurs de fonction de C++. Existe-t-il une fonctionnalité similaire en Java? Étant donné que les pointeurs sont quelque peu absents, quelle est la meilleure façon de procéder? Et pour être clair, nous parlons de première classe ici.

147
Ben Lakey

L'idiome Java pour une fonctionnalité semblable à un pointeur de fonction est une classe anonyme implémentant une interface, par exemple.

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

Mise à jour: Ce qui précède est nécessaire dans Java versions antérieures à Java 8.) Nous avons maintenant de bien meilleures alternatives, à savoir lambdas:

list.sort((a, b) -> a.isGreaterThan(b));

et références de méthodes:

list.sort(MyClass::isGreaterThan);
125
Michael Borgwardt

Vous pouvez remplacer un pointeur de fonction par une interface. Disons que vous voulez parcourir une collection et faire quelque chose avec chaque élément.

public interface IFunction {
  public void execute(Object o);
}

C’est l’interface que nous pourrions transmettre à certains, tels que CollectionUtils2.doFunc (Collection c, IFunction f).

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

Par exemple, disons que nous avons une collection de nombres et que vous souhaitez ajouter 1 à chaque élément.

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});
63
raupach

Vous pouvez utiliser la réflexion pour le faire.

Passez en paramètre l'objet et le nom de la méthode (sous forme de chaîne), puis appelez la méthode. Par exemple:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

Et puis l'utiliser comme dans:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

Bien sûr, vérifiez toutes les exceptions et ajoutez les conversions nécessaires.

41
zoquete

Non, les fonctions ne sont pas des objets de première classe en Java. Vous pouvez faire la même chose en implémentant une classe de gestionnaire - c’est ainsi que les rappels sont implémentés dans Swing, etc.

Il existe cependant des propositions de fermetures (le nom officiel de ce dont vous parlez) dans les futures versions de Java - Javaworld a un article intéressant.

20
jwoolard

Cela me fait penser à Exécution dans le royaume des noms de Steve Yegge. Il indique en gros que Java a besoin d'un objet pour chaque action et qu'il n'a donc pas d'entités "verbales" comme les pointeurs de fonction.

15
Yuval F

Pour obtenir des fonctionnalités similaires, vous pouvez utiliser des classes internes anonymes.

Si vous deviez définir une interface Foo:

interface Foo {
    Object myFunc(Object arg);
}

Créez une méthode bar qui recevra un 'pointeur de fonction' en argument:

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

Enfin, appelez la méthode comme suit:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}
8
Kingamajick

Java8 a introduit lambdas et références de méthodes . Donc, si votre fonction correspond à un interface fonctionnelle (vous pouvez créer la vôtre), vous pouvez utiliser une référence de méthode dans ce cas.

Java fournit un ensemble de interfaces fonctionnelles communes . alors que vous pourriez faire ce qui suit:

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}
6
Alex

Il n'y a rien de tel en Java. Vous devrez envelopper votre fonction dans un objet et passer la référence à cet objet afin de transmettre la référence à la méthode sur cet objet.

Syntaxiquement, cela peut être facilité dans une certaine mesure en utilisant des classes anonymes définies sur place ou anonymes définies en tant que variables membres de la classe.

Exemple:

class MyComponent extends JPanel {
    private JButton button;
    public MyComponent() {
        button = new JButton("click me");
        button.addActionListener(buttonAction);
        add(button);
    }

    private ActionListener buttonAction = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // handle the event...
            // note how the handler instance can access 
            // members of the surrounding class
            button.setText("you clicked me");
        }
    }
}
5
VoidPointer

J'ai mis en place un support callback/délégué dans Java en utilisant la réflexion. Les détails et la source de travail sont disponibles sur mon site web .

Comment ça marche

Nous avons une classe principale appelée Callback avec une classe imbriquée nommée WithParms. L'API nécessitant le rappel prendra un objet Callback en tant que paramètre et, si nécessaire, créera un Callback.WithParms en tant que variable de méthode. Comme un grand nombre d'applications de cet objet seront récursives, cela fonctionne très proprement.

Les performances étant toujours une priorité pour moi, je ne voulais pas être obligé de créer un tableau d'objets jetables pour contenir les paramètres de chaque appel. Après tout, dans une grande structure de données, il pouvait y avoir des milliers d'éléments et un traitement de message. scénario, nous pourrions finir par traiter des milliers de structures de données par seconde.

Pour être threadsafe, le tableau de paramètres doit exister de manière unique pour chaque appel de la méthode API, et pour des raisons d'efficacité, le même doit être utilisé pour chaque appel du rappel; J'avais besoin d'un deuxième objet qui serait peu coûteux à créer afin de lier le rappel à un tableau de paramètres pour l'invocation. Mais, dans certains scénarios, l'appelant aurait déjà un tableau de paramètres pour d'autres raisons. Pour ces deux raisons, le tableau de paramètres n'appartenait pas à l'objet Callback. De plus, le choix de l'invocation (passer les paramètres sous forme de tableau ou d'objets individuels) appartient à l'API utilisant le callback, ce qui lui permet d'utiliser l'invocation qui convient le mieux à son fonctionnement interne.

La classe imbriquée WithParms est donc facultative et sert à deux fins. Elle contient le tableau de paramètres objet nécessaire aux invocations de rappel et fournit 10 méthodes invoke () surchargées (avec de 1 à 10 paramètres) qui chargent le tableau de paramètres, puis invoquer la cible de rappel.

3
Lawrence Dol

Vérifiez les fermetures de la manière dont elles ont été mises en œuvre dans la bibliothèque lambdaj. Ils ont en fait un comportement très similaire à celui des délégués C #:

http://code.google.com/p/lambdaj/wiki/Closures

2
Mario Fusco

Par rapport à la plupart des gens ici, je suis nouveau à Java mais comme je n’ai pas vu une suggestion semblable, j’ai une autre alternative à suggérer. Je ne sais pas si c’est une bonne pratique ou non, ni même suggéré. avant et je ne l’avais pas compris, j’aime juste parce que je pense que c’est auto-descriptif.

 /*Just to merge functions in a common name*/
 public class CustomFunction{ 
 public CustomFunction(){}
 }

 /*Actual functions*/
 public class Function1 extends CustomFunction{
 public Function1(){}
 public void execute(){...something here...}
 }

 public class Function2 extends CustomFunction{
 public Function2(){}
 public void execute(){...something here...}
 }

 .....
 /*in Main class*/
 CustomFunction functionpointer = null;

puis, en fonction de l'application, attribuez

 functionpointer = new Function1();
 functionpointer = new Function2();

etc.

et appeler par

 functionpointer.execute();
0
ozgeneral