Metodo Java Pass come parametro

Sto cercando un modo per passare un metodo per riferimento. Capisco che Java non passa i metodi come parametri, tuttavia, mi piacerebbe ottenere un’alternativa.

Mi è stato detto che le interfacce sono l’alternativa ai metodi di passaggio come parametri, ma non capisco come un’interfaccia possa fungere da metodo per riferimento. Se capisco correttamente un’interfaccia è semplicemente un insieme astratto di metodi che non sono definiti. Non voglio inviare un’interfaccia che deve essere definita ogni volta perché molti metodi diversi potrebbero chiamare lo stesso metodo con gli stessi parametri.

Quello che vorrei realizzare è qualcosa di simile a questo:

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 } 

invocato come:

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

Modifica : a partire da Java 8, le espressioni lambda sono una bella soluzione come hanno sottolineato altre risposte . La risposta qui sotto è stata scritta per Java 7 e precedenti …


Dai un’occhiata al modello di comando .

 // 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.toString()); } } public static void callCommand(Command command, Object data) { command.execute(data); } public static void main(String... args) { callCommand(new PrintCommand(), "hello world"); } } 

Modifica: come sottolinea Pete Kirkham , c’è un altro modo per farlo usando un visitatore . L’approccio per i visitatori è un po ‘più acceptVisitor() : i tuoi nodes devono essere tutti consapevoli dei visitatori con un metodo acceptVisitor() , ma se hai bisogno di attraversare un grafo di oggetti più complesso allora vale la pena esaminare.

In Java 8, ora puoi passare un metodo più facilmente utilizzando Lambda Expressions . Innanzitutto, un po ‘di background. Un’interfaccia funzionale è un’interfaccia che ha uno e un solo metodo astratto, sebbene possa contenere un numero qualsiasi di metodi predefiniti (nuovi in ​​Java 8) e metodi statici. Un’espressione lambda può implementare rapidamente il metodo astratto, senza tutte le inutili syntax necessarie se non si utilizza un’espressione lambda.

Senza espressioni lambda:

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

Con espressioni lambda:

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

Ecco un estratto dal tutorial Java su Lambda Expressions :

Sintassi di Lambda Expressions

Un’espressione lambda è composta da quanto segue:

  • Un elenco separato da virgole di parametri formali racchiusi tra parentesi. Il metodo CheckPerson.test contiene un parametro, p, che rappresenta un’istanza della class Person.

    Nota : è ansible omettere il tipo di dati dei parametri in un’espressione lambda. Inoltre, puoi omettere le parentesi se c’è un solo parametro. Ad esempio, anche la seguente espressione lambda è valida:

     p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() < = 25 
  • Il token della freccia, ->

  • Un corpo, che consiste in una singola espressione o un blocco di istruzioni. Questo esempio utilizza la seguente espressione:

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

    Se si specifica una singola espressione, il runtime Java valuta l'espressione e ne restituisce il valore. In alternativa, puoi usare una dichiarazione di reso:

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

    Una dichiarazione di ritorno non è un'espressione; in un'espressione lambda, è necessario includere istruzioni in parentesi graffe ({}). Tuttavia, non è necessario albind un'invocazione del metodo di annullamento in parentesi graffe. Ad esempio, la seguente è un'espressione lambda valida:

     email -> System.out.println(email) 

Si noti che un'espressione lambda assomiglia molto a una dichiarazione di metodo; puoi considerare espressioni lambda come metodi anonimi - metodi senza un nome.


Ecco come puoi "passare un metodo" usando un'espressione 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)); } } 

Usa l’object java.lang.reflect.Method e chiama invoke

Prima definisci un’interfaccia con il metodo che vuoi passare come parametro

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

Implementa una class con il metodo

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

// Invoca così

 Callable cmd = new Test(); 

Ciò consente di passare cmd come parametro e richiamare la chiamata al metodo definita nell’interfaccia

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

L’ultima volta che ho controllato, Java non è in grado di fare in modo nativo quello che vuoi; devi usare ‘work-arounds’ per aggirare tali limiti. Per quanto vedo, le interfacce SONO un’alternativa, ma non una buona alternativa. Forse chi ti ha detto che intendeva qualcosa del genere:

 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 } 

Che quindi invocherai con:

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

Anche se questo non è ancora valido per Java 7 e versioni successive, credo che dovremmo guardare al futuro e almeno riconoscere i cambiamenti in arrivo in nuove versioni come Java 8.

Vale a dire, questa nuova versione porta lambda e riferimenti ai metodi in Java (insieme alle nuove API , che sono un’altra valida soluzione a questo problema, mentre richiedono ancora un’interfaccia non vengono creati nuovi oggetti e file di class extra non devono inquinare le directory di output a causa di diversi gestione da parte della JVM.

Entrambi i sapori (lambda e metodo di riferimento) richiedono un’interfaccia disponibile con un singolo metodo la cui firma è utilizzata:

 public interface NewVersionTest{ String returnAString(Object oIn, String str); } 

I nomi dei metodi non contano da qui in poi. Dove viene accettato un lambda, è anche un riferimento al metodo. Ad esempio, per utilizzare la nostra firma qui:

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

Questa è solo una semplice invocazione dell’interfaccia, fino a quando lambda 1 non viene passato:

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

Questo produrrà:

 Lambda reached! lambda return 

I riferimenti al metodo sono simili. Dato:

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

e principale:

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

l’output sarebbe un real static method . I riferimenti al metodo possono essere statici, istanza, non statici con istanze arbitrarie e persino costruttori . Per il costruttore sarebbe stato usato qualcosa di simile a ClassName::new .

1 Questo non è considerato un lambda da alcuni, in quanto ha effetti collaterali. Illustra, tuttavia, l’uso di uno in modo più diretto per la visualizzazione.

Se non hai bisogno di questi metodi per restituire qualcosa, puoi renderli restituibili Oggetti eseguibili.

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

Quindi usarlo come:

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

Da Java 8 c’è una Function interface ( docs ), che ha un metodo

 R apply(T t); 

Puoi usarlo per passare funzioni come parametri ad altre funzioni. T è il tipo di input della funzione, R è il tipo di ritorno.

Nel tuo esempio devi passare una funzione che prende tipo Component come input e non restituisce nulla – Void . In questo caso la Function non è la scelta migliore, dal momento che non esiste autoboxing del tipo Void. L’interfaccia che stai cercando si chiama Consumer ( docs ) con method

 void accept(T t); 

Sarebbe come questo:

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

E lo chiameresti usando i riferimenti al metodo:

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

Supponendo di aver definito i metodi changeColor () e changeSize () nella stessa class.


Se il tuo metodo accetta più di un parametro, puoi utilizzare BiFunction – T e U come tipi di parametri di input e R come tipo di ritorno. C’è anche BiConsumer (due argomenti, nessun tipo di ritorno). Sfortunatamente per 3 e più parametri di input, devi creare un’interfaccia da solo. Per esempio:

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

Usa il pattern Observer (a volte chiamato anche listener 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()... dichiara un tipo anonimo che implementa l’interfaccia.

Java ha un meccanismo per passare il nome e chiamarlo. Fa parte del meccanismo di riflessione. La tua funzione dovrebbe prendere parametri aggiuntivi del metodo di class.

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

Ecco un esempio di 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(String[] arguments) { performTask(TestMethodPassing::println); performTask(TestMethodPassing::print); } } 

Produzione:

 Do println Do print 

Non sono un esperto di java ma risolvo il tuo problema in questo modo:

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

Definisco il parametro nella mia interfaccia speciale

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

Infine, implemento il metodo getSearchText durante la chiamata al metodo di inizializzazione .

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

Non penso che i lambda siano pensati per questo … Java non è un linguaggio di programmazione funzionale e non lo sarà mai, non passiamo i metodi come parametri. Detto questo, ricorda che Java è orientato agli oggetti e con questo in mente possiamo fare tutto ciò che vogliamo. La prima idea è semplicemente passare un “object che contiene un metodo” come parametro. Quindi, ogni volta che devi “passare” il metodo, passa semplicemente un’istanza di quella class. Si noti che quando si definisce il metodo, è necessario aggiungere come parametro un’istanza della class che contiene il metodo. Questo dovrebbe funzionare ma non è quello che vogliamo perché non è ansible ridefinire il metodo se non si ha accesso al codice di class e ciò non è ansible in molti casi; inoltre, penso che se qualcuno ha bisogno di passare un metodo come parametro è perché il comportamento del metodo dovrebbe essere dinamico. Quello che voglio dire è che il programmatore che usa le tue classi dovrebbe essere in grado di scegliere il metodo da restituire ma non il suo tipo. Fortunatamente per noi Java ha una soluzione bella e semplice: classi astratte. Le classi astratte, in poche parole, vengono utilizzate quando si conosce la “firma” di un metodo “ma non il suo comportamento … È ansible racchiudere il nome e il tipo del metodo in una class astratta e passare un’istanza di tale class come parametro per il metodo … Aspetta … non è la stessa cosa di prima? E puoi avere un’istanza di una class astratta? No e No … ma anche sì … quando crei un metodo astratto devi anche ridefinirlo in una class che estende la class astratta e, a causa del collegamento dinamico di java, Java sarà sempre (a meno che tu lo dichiari statico, privato e altre cose) utilizzi la versione ridefinita di esso. Ecco un esempio … Supponiamo di voler applicare una funzione a una serie di numeri: quindi se vogliamo quadrare l’input-output dovrebbe apparire così [1,2,3,4, …] -> [1,4,9,16 , …] (in un linguaggio di programmazione funzionale come haskell questo è facile grazie ad alcuni strumenti come ‘mappa’, …). Si noti che non c’è nulla di speciale nel quadrare i numeri, potremmo applicare qualsiasi funzione che noi desideriamo t. Quindi il codice dovrebbe essere qualcosa come questo [args], f -> [f (args)]. Torna a java => una funzione è solo un metodo, quindi ciò che vogliamo è una funzione che applica un’altra funzione a una matrice. In poche parole, abbiamo bisogno di passare un metodo come parametro. Ecco come lo farei ==>

1) DEFINIRE LA CLASSE ABSTRACT DEL WRAPPER E IL METODO

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

2) DEFINIRE LA CLASSE CON IL METODO APPLY_TO_ARRAY

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

3) CREA UNA CLASSE DI TESTER E HANNO ALCUNI DIVERTIMENTO

 public class Testclass extends Function { public static void main(String[] 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); } } 

Si noti che è necessario passare un object di tipo Function e poiché Testclass estende la class Function che è ansible utilizzarlo, il cast è automatico.