Qual è il sostituto più vicino per un puntatore a funzione in Java?

Ho un metodo che è circa dieci righe di codice. Voglio creare più metodi che facciano esattamente la stessa cosa, ad eccezione di un piccolo calcolo che cambierà una riga di codice. Questa è un’applicazione perfetta per passare un puntatore a una funzione per sostituire quella riga, ma Java non ha puntatori di funzione. Qual è la mia migliore alternativa?

Classe interiore anonima

Supponiamo che tu voglia far passare una funzione con un parametro String che restituisce un int .
Per prima cosa devi definire un’interfaccia con la funzione come suo unico membro, se non puoi riutilizzarne una esistente.

 interface StringFunction { int func(String param); } 

Un metodo che accetta il puntatore accetta solo un’istanza di StringFunction modo:

 public void takingMethod(StringFunction sf) { int i = sf.func("my string"); // do whatever ... } 

E si chiamerebbe così:

 ref.takingMethod(new StringFunction() { public int func(String param) { // body } }); 

EDIT: in Java 8, puoi chiamarlo con un’espressione lambda:

 ref.takingMethod(param -> bodyExpression); 

Per ogni “puntatore di funzione”, creerei una piccola class di functor che implementa il tuo calcolo. Definisci un’interfaccia che tutte le classi implementeranno e passa le istanze di tali oggetti nella tua funzione più ampia. Questa è una combinazione del ” modello di comando ” e del ” modello di strategia “.

L’esempio di @ sblundy è buono.

Quando c’è un numero predefinito di calcoli diversi che puoi fare in quella linea, usare un enum è un modo rapido, ma chiaro per implementare un modello di strategia.

 public enum Operation { PLUS { public double calc(double a, double b) { return a + b; } }, TIMES { public double calc(double a, double b) { return a * b; } } ... public abstract double calc(double a, double b); } 

Ovviamente, la dichiarazione del metodo di strategia, così come esattamente un’istanza di ciascuna implementazione sono tutte definite in una singola class / file.

Devi creare un’interfaccia che fornisca le funzioni che vuoi trasmettere. per esempio:

 /** * A simple interface to wrap up a function of one argument. * * @author rcreswick * */ public interface Function1 { /** * Evaluates this function on it's arguments. * * @param a The first argument. * @return The result. */ public S eval(T a); } 

Quindi, quando è necessario passare una funzione, è ansible implementare tale interfaccia:

 List result = CollectionUtilities.map(list, new Function1() { @Override public Integer eval(Integer a) { return a * a; } }); 

Infine, la funzione mappa utilizza il passato in Function1 come segue:

  public static  Map zipWith(Function2 fn, Map m1, Map m2, Map results){ Set keySet = new HashSet(); keySet.addAll(m1.keySet()); keySet.addAll(m2.keySet()); results.clear(); for (K key : keySet) { results.put(key, fn.eval(m1.get(key), m2.get(key))); } return results; } 

Puoi spesso usare Runnable invece della tua interfaccia se non hai bisogno di passare i parametri, oppure puoi usare varie altre tecniche per rendere il parametro meno “fisso” ma di solito è un compromesso con la sicurezza del tipo. (Oppure puoi sovrascrivere la funzione di costruzione affinché il tuo object funzione passi i parametri in questo modo … ci sono molti approcci e alcuni funzionano meglio in determinate circostanze.)

Puoi anche farlo (che in alcune occasioni RARE ha senso). Il problema (ed è un grosso problema) è che si perde tutta la tipesafety dell’uso di una class / interfaccia e si ha a che fare con il caso in cui il metodo non esiste.

Ha il “vantaggio” che è ansible ignorare le restrizioni di accesso e chiamare metodi privati ​​(non mostrati nell’esempio, ma è ansible chiamare metodi che il compilatore normalmente non consente di chiamare).

Ancora una volta, è raro che ciò abbia senso, ma in quelle occasioni è uno strumento piacevole da avere.

 import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; class Main { public static void main(final String[] argv) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { final String methodName; final Method method; final Main main; main = new Main(); if(argv.length == 0) { methodName = "foo"; } else { methodName = "bar"; } method = Main.class.getDeclaredMethod(methodName, int.class); main.car(method, 42); } private void foo(final int x) { System.out.println("foo: " + x); } private void bar(final int x) { System.out.println("bar: " + x); } private void car(final Method method, final int val) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { method.invoke(this, val); } } 

Riferimenti al metodo usando l’operatore ::

È ansible utilizzare i riferimenti al metodo negli argomenti del metodo in cui il metodo accetta un’interfaccia funzionale . Un’interfaccia funzionale è un’interfaccia che contiene solo un metodo astratto. (Un’interfaccia funzionale può contenere uno o più metodi predefiniti o metodi statici.)

IntBinaryOperator è un’interfaccia funzionale. Il suo metodo astratto, applyAsInt , accetta due int come parametri e restituisce un int . Math.max accetta anche due int e restituisce un int . In questo esempio, A.method(Math::max); rende parameter.applyAsInt invia i suoi due valori di input a Math.max e restituisce il risultato di tale Math.max .

 import java.util.function.IntBinaryOperator; class A { static void method(IntBinaryOperator parameter) { int i = parameter.applyAsInt(7315, 89163); System.out.println(i); } } 
 import java.lang.Math; class B { public static void main(String[] args) { A.method(Math::max); } } 

In generale, puoi usare:

 method1(Class1::method2); 

invece di:

 method1((arg1, arg2) -> Class1.method2(arg1, arg2)); 

che è l’abbreviazione di:

 method1(new Interface1() { int method1(int arg1, int arg2) { return Class1.method2(arg1, agr2); } }); 

Per ulteriori informazioni, consultare l’ operatore :: (doppio punto) in Java 8 e nella specifica del linguaggio Java §15.13 .

Se si dispone di una sola riga diversa è ansible aggiungere un parametro, ad esempio un flag e un’istruzione if (flag) che chiama una linea o l’altra.

Potresti anche essere interessato a sapere del lavoro in corso per Java 7 che riguarda le chiusure:

Qual è lo stato attuale delle chiusure in Java?

http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures

La risposta di @ sblundy è ottima, ma le classi interne anonime hanno due piccoli difetti, il principale è che tendono a non essere riusabili e il secondario è una syntax voluminosa.

La cosa bella è che il suo schema si espande in classi complete senza alcun cambiamento nella class principale (quella che esegue i calcoli).

Quando istanziate una nuova class, potete passare parametri in quella class che può agire da costanti nella vostra equazione – quindi se una delle vostre classi interne assomiglia a questa:

 f(x,y)=x*y 

ma a volte ne hai bisogno uno che sia:

 f(x,y)=x*y*2 

e forse un terzo che è:

 f(x,y)=x*y/2 

piuttosto che creare due classi interne anonime o aggiungere un parametro “passthrough”, puoi creare una singola class ACTUAL che istanziati come:

 InnerFunc f=new InnerFunc(1.0);// for the first calculateUsing(f); f=new InnerFunc(2.0);// for the second calculateUsing(f); f=new InnerFunc(0.5);// for the third calculateUsing(f); 

Dovrebbe semplicemente memorizzare la costante nella class e usarla nel metodo specificato nell’interfaccia.

In effetti, se SAPI che la tua funzione non verrà memorizzata / riutilizzata, potresti fare questo:

 InnerFunc f=new InnerFunc(1.0);// for the first calculateUsing(f); f.setConstant(2.0); calculateUsing(f); f.setConstant(0.5); calculateUsing(f); 

Ma le classi immutabili sono più sicure – non riesco a trovare una giustificazione per rendere una class come questa mutevole.

Lo postò solo perché mi sento rabbioso ogni volta che ascolto una class interiore anonima – ho visto un sacco di codice ridondante che era “Richiesto” perché la prima cosa che faceva il programmatore era andare anonimo quando avrebbe dovuto usare una class reale e mai ripensò alla sua decisione.

Nuove interfacce funzionali di Java 8 e riferimenti al metodo che utilizzano l’operatore :: .

Java 8 è in grado di conservare i riferimenti al metodo (MyClass :: new) con i puntatori ” @ Functional Interface “. Non è necessario lo stesso nome del metodo, è richiesta solo la stessa firma del metodo.

Esempio:

 @FunctionalInterface interface CallbackHandler{ public void onClick(); } public class MyClass{ public void doClick1(){System.out.println("doClick1");;} public void doClick2(){System.out.println("doClick2");} public CallbackHandler mClickListener = this::doClick; public static void main(String[] args) { MyClass myObjectInstance = new MyClass(); CallbackHandler pointer = myObjectInstance::doClick1; Runnable pointer2 = myObjectInstance::doClick2; pointer.onClick(); pointer2.run(); } } 

Allora, cosa abbiamo qui?

  1. Interfaccia funzionale: interfaccia, annotata o non con @FunctionalInterface , che contiene solo una dichiarazione di metodo.
  2. Riferimenti al metodo – questa è solo una syntax speciale, assomiglia a questo, objectInstance :: methodName , niente più niente di meno.
  3. Esempio di utilizzo: solo un operatore di assegnazione e quindi una chiamata al metodo di interfaccia.

DEVI UTILIZZARE INTERFACCE FUNZIONALI PER ASCOLTANTI SOLO E SOLO PER QUESTO!

Perché tutti gli altri puntatori a funzioni di questo tipo sono davvero dannosi per la leggibilità del codice e per la capacità di comprensione. Tuttavia, i riferimenti diretti al metodo a volte sono utili, ad esempio con foreach.

Esistono diverse interfacce funzionali predefinite:

 Runnable -> void run( ); Supplier -> T get( ); Consumer -> void accept(T); Predicate -> boolean test(T); UnaryOperator -> T apply(T); BinaryOperator -> R apply(T, U); Function -> R apply(T); BiFunction -> R apply(T, U); //... and some more of it ... Callable -> V call() throws Exception; Readable -> int read(CharBuffer) throws IOException; AutoCloseable -> void close() throws Exception; Iterable -> Iterator iterator(); Comparable -> int compareTo(T); Comparator -> int compare(T,T); 

Per le versioni precedenti di Java dovresti provare le Librerie Guava, che hanno funzionalità e syntax simili, come Adrian Petrescu ha menzionato sopra.

Per ulteriori ricerche, guarda il Cheatsheet di Java 8

e grazie a The Guy con il collegamento The Hat for the Java Language Specification §15.13 .

Le librerie Google Guava , che stanno diventando molto popolari, hanno un object generico Funzione e predicato che hanno lavorato in molte parti della loro API.

Mi sembra un modello strategico. Controlla i modelli Java di fluffycat.com.

Una delle cose che mi manca davvero quando programmare in Java è la funzione di callback. Una situazione in cui la necessità di questi continuava a presentarsi era nell’elaborare ricorsivamente le gerarchie in cui si desidera eseguire un’azione specifica per ciascun elemento. Come camminare su un albero di directory o elaborare una struttura di dati. Il minimalista dentro di me odia dover definire un’interfaccia e quindi un’implementazione per ogni caso specifico.

Un giorno mi sono ritrovato a chiedermi perché no? Abbiamo dei puntatori del metodo: l’object Method. Con l’ottimizzazione dei compilatori JIT, l’invocazione riflessiva non comporta più un’enorme penalizzazione delle prestazioni. E oltre a, ad esempio, copiare un file da una posizione a un’altra, il costo dell’invocazione del metodo riflesso diventa insignificante.

Come ho pensato di più, mi sono reso conto che un callback nel paradigma OOP richiede l’associazione di un object e un metodo insieme – immettere l’object Callback.

Controlla la mia soluzione basata su reflection per Callbacks in Java . Gratuito per qualsiasi uso.

oK, questo thread è già abbastanza vecchio, quindi molto probabilmente la mia risposta non è utile per la domanda. Ma poiché questo thread mi ha aiutato a trovare la mia soluzione, la metterò qui comunque.

Avevo bisogno di usare un metodo statico variabile con input noto e output noto (entrambi double ). Quindi, sapendo il pacchetto e il nome del metodo, potrei lavorare come segue:

 java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params); 

per una funzione che accetta un doppio come parametro.

Quindi, nella mia situazione concreta, l’ho inizializzato con

 java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class); 

e lo ha invocato in seguito in una situazione più complessa con

 return (java.lang.Double)this.Function.invoke(null, args); java.lang.Object[] args = new java.lang.Object[] {activity}; someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args); 

dove l’attività è un doppio valore arbitrario. Sto pensando di farlo un po ‘più astratto e generalizzando, come ha fatto SoftwareMonkey, ma al momento sono abbastanza soddisfatto per come è. Tre linee di codice, nessuna class e interfaccia necessarie, non è male.

Per fare la stessa cosa senza interfacce per una serie di funzioni:

 class NameFuncPair { public String name; // name each func void f(String x) {} // stub gets overridden public NameFuncPair(String myName) { this.name = myName; } } public class ArrayOfFunctions { public static void main(String[] args) { final A a = new A(); final B b = new B(); NameFuncPair[] fArray = new NameFuncPair[] { new NameFuncPair("A") { @Override void f(String x) { ag(x); } }, new NameFuncPair("B") { @Override void f(String x) { bh(x); } }, }; // Go through the whole func list and run the func named "B" for (NameFuncPair fInstance : fArray) { if (fInstance.name.equals("B")) { fInstance.f(fInstance.name + "(some args)"); } } } } class A { void g(String args) { System.out.println(args); } } class B { void h(String args) { System.out.println(args); } } 

Dai un’occhiata a lambdaj

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

e in particolare la sua nuova funzione di chiusura

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

e troverai un modo molto leggibile per definire il puntatore di chiusura o di funzione senza creare un’interfaccia priva di significato o usare brutte classi interne

Wow, perché non creare una class Delegate che non è poi così difficile dato che l’ho già fatto per java e lo uso per passare in parametro dove T è il tipo restituito. Mi dispiace ma come programmatore C ++ / C # in generale sto imparando java, ho bisogno di puntatori di funzione perché sono molto utili. Se hai familiarità con qualsiasi class che si occupa di Informazioni sul Metodo puoi farlo. Nelle librerie java sarebbe java.lang.reflect.method.

Se si utilizza sempre un’interfaccia, è sempre necessario implementarla. In eventhandling non c’è davvero un modo migliore per registrare / annullare la registrazione dall’elenco di gestori, ma per i delegati in cui è necessario passare le funzioni e non il tipo di valore, facendo una class delegata per gestirle per surclassare un’interfaccia.

Se qualcuno sta lottando per passare una funzione che richiede un set di parametri per definire il suo comportamento ma un altro insieme di parametri su cui eseguire, come Scheme:

 (define (function scalar1 scalar2) (lambda (x) (* x scalar1 scalar2))) 

vedere Funzione di passaggio con comportamento definito da parametri in Java

Dal momento che Java8, è ansible utilizzare lambdas, che dispone anche di librerie nell’API SE 8 ufficiale.

Utilizzo: è necessario utilizzare un’interfaccia con un solo metodo astratto. Crea un’istanza di esso (potresti voler usare l’unico java SE 8 già fornito) in questo modo:

 Function functionname = (inputvariablename) { ... return outputinstance; } 

Per ulteriori informazioni, consultare la documentazione: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

Prima di Java 8, il sostituto più vicino alla funzionalità del puntatore a funzione era una class anonima. Per esempio:

 Collections.sort(list, new Comparator(){ public int compare(CustomClass a, CustomClass b) { // Logic to compare objects of class CustomClass which returns int as per contract. } }); 

Ma ora in Java 8 abbiamo un’alternativa molto pulita conosciuta come espressione lambda , che può essere usata come:

 list.sort((a, b) -> { a.isBiggerThan(b) } ); 

dove isBiggerThan è un metodo in CustomClass . Possiamo anche utilizzare i riferimenti al metodo qui:

 list.sort(MyClass::isBiggerThan); 

Nessuna delle risposte a Java 8 ha fornito un esempio completo e coerente, quindi ecco qui.

Dichiarare il metodo che accetta il “puntatore di funzione” come segue:

 void doCalculation(Function calculation, int parameter) { final String result = calculation.apply(parameter); } 

Chiamalo fornendo la funzione con un’espressione lambda:

 doCalculation((i) -> i.toString(), 2);