Méthode Java Pass en tant que paramètre

Je cherche un moyen de passer une méthode par référence. Je comprends que Java ne passe pas les méthodes en tant que parameters, cependant, je voudrais avoir une alternative.

On m’a dit que les interfaces sont l’alternative à la transmission de méthodes en tant que parameters, mais je ne comprends pas comment une interface peut servir de méthode par référence. Si je comprends bien, une interface est simplement un ensemble abstrait de méthodes qui ne sont pas définies. Je ne veux pas envoyer une interface qui doit être définie à chaque fois, car plusieurs méthodes différentes peuvent appeler la même méthode avec les mêmes parameters.

Ce que je voudrais accomplir est quelque chose de similaire à ceci:

public void setAllComponents(Component[] myComponentArray, Method myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { //recursive call if Container Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } //end if node myMethod(leaf); } //end looping through components } 

invoqué tel que:

 setAllComponents(this.getComponents(), changeColor()); setAllComponents(this.getComponents(), changeSize()); 

Edit : à partir de Java 8, les expressions lambda sont une solution intéressante, comme d’ autres réponses l’ ont indiqué. La réponse ci-dessous a été écrite pour Java 7 et versions antérieures …


Jetez un coup d’oeil au modèle de commande .

 // NOTE: code not tested, but I believe this is valid java... public class CommandExample { public interface Command { public void execute(Object data); } public class PrintCommand implements Command { public void execute(Object data) { System.out.println(data.toSsortingng()); } } public static void callCommand(Command command, Object data) { command.execute(data); } public static void main(Ssortingng... args) { callCommand(new PrintCommand(), "hello world"); } } 

Edit: comme le souligne Pete Kirkham , il y a une autre façon de faire cela avec un visiteur . L’approche par les visiteurs est un peu plus complexe – vos nœuds doivent tous être sensibles aux visiteurs avec une méthode acceptVisitor() – mais si vous devez parcourir un graphe d’objects plus complexe, cela vaut la peine d’être examiné.

Dans Java 8, vous pouvez désormais passer plus facilement une méthode à l’aide des expressions Lambda . Tout d’abord, un peu de contexte. Une interface fonctionnelle est une interface qui possède une et une seule méthode abstraite, même si elle peut contenir un nombre quelconque de méthodes par défaut (nouveau dans Java 8) et des méthodes statiques. Une expression lambda peut rapidement implémenter la méthode abstraite, sans toute la syntaxe inutile nécessaire si vous n’utilisez pas une expression lambda.

Sans les 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 du didacticiel Java sur les expressions Lambda :

Syntaxe des expressions lambda

Une expression lambda comprend les éléments suivants:

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

    Remarque : Vous pouvez omettre le type de données des parameters dans une expression lambda. De plus, 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 constitué d'une seule expression ou d'un bloc d'instructions. Cet exemple utilise l'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 puis renvoie sa valeur. Vous pouvez également utiliser une déclaration de retour:

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

    Une déclaration de retour n'est pas une expression; dans une expression lambda, vous devez inclure des instructions entre accolades ({}). Cependant, vous n'avez pas besoin d'inclure une invocation de méthode vide dans les accolades. Par exemple, 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 comme des méthodes anonymes - méthodes sans nom.


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

 interface I { public void myMethod(Component component); } class A { public void changeColor(Component component) { // code here } public void changeSize(Component component) { // code here } } class B { public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) { for(Component leaf : myComponentArray) { if(leaf instanceof Container) { // recursive call if Container Container node = (Container)leaf; setAllComponents(node.getComponents(), myMethodInterface); } // end if node myMethodsInterface.myMethod(leaf); } // end looping through components } } class C { A a = new A(); B b = new B(); public C() { b.setAllComponents(this.getComponents(), component -> a.changeColor(component)); b.setAllComponents(this.getComponents(), component -> a.changeSize(component)); } } 

Utilisez l’object java.lang.reflect.Method et appelez invoke

Définissez d’abord une interface avec la méthode que vous souhaitez passer en paramètre

 public interface Callable { public void call(int param); } 

Implémenter une classe avec la méthode

 class Test implements Callable { public void call(int param) { System.out.println( param ); } } 

// Invoque comme ça

 Callable cmd = new Test(); 

Cela vous permet de passer cmd en paramètre et d’appeler l’appel de méthode défini dans l’interface

 public invoke( Callable callable ) { callable.call( 5 ); } 

La dernière fois que j’ai vérifié, Java n’est pas capable de faire nativement ce que vous voulez; vous devez utiliser des solutions de rechange pour contourner ces limitations. Pour moi, les interfaces sont une alternative, mais pas une bonne alternative. Peut-être que celui qui vous a dit cela signifiait quelque chose comme ceci:

 public interface ComponentMethod { public abstract void PerfromMethod(Container c); } public class ChangeColor implements ComponentMethod { @Override public void PerfromMethod(Container c) { // do color change stuff } } public class ChangeSize implements ComponentMethod { @Override public void PerfromMethod(Container c) { // do color change stuff } } public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { //recursive call if Container Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } //end if node myMethod.PerfromMethod(leaf); } //end looping through components } 

Que vous invoqueriez alors avec:

 setAllComponents(this.getComponents(), new ChangeColor()); setAllComponents(this.getComponents(), new ChangeSize()); 

Bien que cela ne soit pas encore valable pour Java 7 et les versions ultérieures, je pense que nous devrions nous tourner vers l’avenir et au moins reconnaître les modifications à apporter dans de nouvelles versions telles que Java 8.

À savoir, cette nouvelle version apporte des références de méthode lambdas et de méthode à Java (avec de nouvelles API , qui constituent une autre solution valable à ce problème. Bien qu’elles nécessitent toujours une interface, aucun nouvel object n’est pollué. manipulation par la JVM.

Les deux versions (lambda et référence de méthode) nécessitent une interface disponible avec une seule méthode dont la signature est utilisée:

 public interface NewVersionTest{ Ssortingng returnASsortingng(Object oIn, Ssortingng str); } 

Les noms des méthodes n’auront plus d’importance d’ici. Si un lambda est accepté, une référence de méthode l’est également. Par exemple, pour utiliser notre signature ici:

 public static void printOutput(NewVersionTest t, Object o, Ssortingng s){ System.out.println(t.returnASsortingng(o, s)); } 

Ceci est juste une simple invocation d’interface, jusqu’à ce que le lambda 1 soit passé:

 public static void main(Ssortingng[] args){ printOutput( (Object oIn, Ssortingng sIn) -> { System.out.println("Lambda reached!"); return "lambda return"; } ); } 

Cela va sortir:

 Lambda reached! lambda return 

Les références de méthode sont similaires. Donné:

 public class HelperClass{ public static Ssortingng testOtherSig(Object o, Ssortingng s){ return "real static method"; } } 

et principal:

 public static void main(Ssortingng[] args){ printOutput(HelperClass::testOtherSig); } 

la sortie serait une real static method . Les références de méthode peuvent être statiques, instance, non statiques avec des instances arbitraires et même des constructeurs . Pour le constructeur, quelque chose semblable à ClassName::new serait utilisé.

1 Ceci n’est pas considéré comme un lambda par certains, car il a des effets secondaires. Cela illustre toutefois l’utilisation d’une méthode plus simple à visualiser.

Si vous n’avez pas besoin de ces méthodes pour renvoyer quelque chose, vous pouvez les faire retourner des objects Runnable.

 private Runnable methodName (final int arg){ return new Runnable(){ public void run(){ // do stuff with arg } } } 

Ensuite, utilisez-le comme:

 private void otherMethodName (Runnable arg){ arg.run(); } 

Depuis Java 8, il existe une interface Function ( docs ), qui a la méthode

 R apply(T t); 

Vous pouvez l’utiliser pour transmettre des fonctions en tant que parameters à d’autres fonctions. T est le type d’entrée de la fonction, R est le type de retour.

Dans votre exemple, vous devez passer une fonction qui prend en entrée le type de Component et ne renvoie rien – Void . Dans ce cas, la Function n’est pas le meilleur choix, car il n’y a pas d’offre automatique de type Void. L’interface que vous recherchez s’appelle Consumer ( docs ) avec la méthode

 void accept(T t); 

Cela ressemblerait à ceci:

 public void setAllComponents(Component[] myComponentArray, Consumer myMethod) { for (Component leaf : myComponentArray) { if (leaf instanceof Container) { Container node = (Container) leaf; setAllComponents(node.getComponents(), myMethod); } myMethod.accept(leaf); } } 

Et vous appelez cela en utilisant les références de méthode:

 setAllComponents(this.getComponents(), this::changeColor); setAllComponents(this.getComponents(), this::changeSize); 

En supposant que vous avez défini les méthodes changeColor () et changeSize () dans la même classe.


Si votre méthode accepte plus d’un paramètre, vous pouvez utiliser BiFunction – T et U étant des types de parameters d’entrée et R étant un type de retour. Il y a aussi BiConsumer (deux arguments, pas de type de retour). Malheureusement pour 3 parameters d’entrée et plus, vous devez créer une interface par vous-même. Par exemple:

 public interface Function4 { R apply(A a, B b, C c, D d); } 

Utilisez le pattern Observer (parfois aussi appelé pattern Listener):

 interface ComponentDelegate { void doSomething(Component component); } public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) { // ... delegate.doSomething(leaf); } setAllComponents(this.getComponents(), new ComponentDelegate() { void doSomething(Component component) { changeColor(component); // or do directly what you want } }); 

new ComponentDelegate()... déclare un type anonyme implémentant l’interface.

Java possède un mécanisme pour transmettre le nom et l’appeler. Cela fait partie du mécanisme de reflection. Votre fonction devrait prendre un paramètre supplémentaire de la méthode Method.

 public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled) { ... Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist); ... } 

Voici un exemple de base:

 public class TestMethodPassing { private static void println() { System.out.println("Do println"); } private static void print() { System.out.print("Do print"); } private static void performTask(BasicFunctionalInterface functionalInterface) { functionalInterface.performTask(); } @FunctionalInterface interface BasicFunctionalInterface { void performTask(); } public static void main(Ssortingng[] arguments) { performTask(TestMethodPassing::println); performTask(TestMethodPassing::print); } } 

Sortie:

 Do println Do print 

Je ne suis pas un expert en java mais je résous votre problème comme ceci:

 @FunctionalInterface public interface AutoCompleteCallable { Ssortingng call(T model) throws Exception; } 

Je définis le paramètre dans mon interface spéciale

 public  void initialize(List ensortinges, AutoCompleteCallable getSearchText) {....... //call here Ssortingng value = getSearchText.call(item); ... } 

Enfin, j’implémente la méthode getSearchText en appelant la méthode initialize .

 initialize(getMessageContactModelList(), new AutoCompleteCallable() { @Override public Ssortingng call(Object model) throws Exception { return "custom ssortingng" + ((xxxModel)model.getTitle()); } }) 

Je ne pense pas que les lambdas soient destinés à cela … Java n’est pas un langage de programmation fonctionnel et ne le sera jamais, nous ne transmettons pas de méthodes en tant que parameters. Cela étant dit, rappelez-vous que Java est orienté object et que nous pouvons faire ce que nous voulons. La première idée est de simplement passer un “object contenant une méthode” en tant que paramètre. Donc, chaque fois que vous avez besoin de “passer” la méthode, passez simplement une instance de cette classe. Notez que lorsque vous définissez la méthode, vous devez append comme paramètre une instance de la classe contenant la méthode. Cela devrait fonctionner mais ce n’est pas ce que nous voulons, car vous ne pouvez pas redéfinir la méthode à moins d’avoir access au code de la classe et ce n’est pas possible dans de nombreux cas. de plus, je pense que si quelqu’un doit passer une méthode en paramètre, c’est que le comportement de la méthode doit être dynamic. Ce que je veux dire, c’est que le programmeur qui utilise vos classes devrait pouvoir choisir ce que la méthode doit retourner, mais pas son type. Heureusement pour nous, Java a une solution simple et belle: les classes abstraites. En résumé, les classes abstraites sont utilisées lorsque vous connaissez la “signature” d’une méthode “mais pas son comportement … Vous pouvez envelopper le nom et le type de la méthode dans une classe abstraite et passer une instance de cette classe en tant que paramètre pour la méthode … Attendez … n’est-ce pas la même chose qu’avant et pouvez-vous avoir une instance d’une classe abstraite? Non et Non … mais aussi oui … lorsque vous créez une méthode abstraite Vous devez également le redéfinir dans une classe qui étend la classe abstraite et, à cause de la liaison dynamic de java, Java utilisera toujours (sauf si vous le déclarez statique, privé et autre) la version redéfinie de celui-ci. Supposons que l’on veuille appliquer une fonction à un tableau de nombres: donc si on veut carré, l’entrée-sortie devrait ressembler à ceci [1,2,3,4, …] -> [1,4,9,16 , …] (dans un langage de programmation fonctionnel comme haskell, c’est facile grâce à des outils tels que ‘map’, …). Notez que les nombres au carré ne sont pas spéciaux, nous pourrions appliquer n’importe quelle fonction que nous souhaitons. t. Donc, le code devrait être quelque chose comme ceci [args], f -> [f (args)]. Retour à java => une fonction est juste une méthode, donc nous voulons une fonction qui applique une autre fonction à un tableau. En bref, nous devons passer une méthode en paramètre. Voici comment je le ferais ==>

1) DÉFINIR LA CLASSE RÉSUMÉE ET LA MÉTHODE

 public abstract class Function { public abstract double f(double x); } 

2) DÉFINIR LA CLASSE AVEC LA MÉTHODE APPLY_TO_ARRAY

 public class ArrayMap { public static double[] apply_to_array(double[] arr, Function fun) { for(int i=0; i 

3) CRÉER UNE CLASSE DE TESTEUR ET AVOIR DU PLAISIR

 public class Testclass extends Function { public static void main(Ssortingng[] args) { double[] myarr = {1,2,3,4}; ArrayMap.apply_to_array(myarr, new Testclass()); for (double k : myarr) { System.out.println(k); } } @Override public double f(double x) { return Math.log(x); } } 

Notez que nous avons besoin de passer un object de type Function et puisque Testclass étend la classe Function, nous pouvons l'utiliser, le cast est automatique.