variable valor salida referencia por pasar parámetros parametros parametro objeto enviar entero datos como java closures function-pointers

valor - ¿Cuál es el sustituto más cercano para un puntero de función en Java?



valor en java (21)

Tengo un método que es cerca de diez líneas de código. Quiero crear más métodos que hagan exactamente lo mismo, excepto por un pequeño cálculo que cambiará una línea de código. Esta es una aplicación perfecta para pasar un puntero de función para reemplazar esa línea, pero Java no tiene punteros de función. ¿Cuál es mi mejor alternativa?


Método de referencias utilizando el operador ::

Puede usar referencias de método en argumentos de método donde el método acepta una interfaz funcional . Una interfaz funcional es cualquier interfaz que contiene un solo método abstracto. (Una interfaz funcional puede contener uno o más métodos predeterminados o métodos estáticos).

IntBinaryOperator es una interfaz funcional. Su método abstracto, applyAsInt , acepta dos int s como sus parámetros y devuelve un int . Math.max también acepta dos int s y devuelve un int . En este ejemplo, A.method(Math::max); hace que parameter.applyAsInt envíe sus dos valores de entrada a Math.max y devuelva el resultado de ese 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); } }

En general, puede utilizar:

method1(Class1::method2);

en lugar de:

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

que es la abreviatura de:

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

Para obtener más información, consulte el operador :: (dos puntos) en Java 8 y Java Language Specification §15.13 .


Antes de Java 8, el sustituto más cercano para la funcionalidad tipo puntero a función era una clase anónima. Por ejemplo:

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

Pero ahora en Java 8 tenemos una alternativa muy clara conocida como https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html , que se puede usar como:

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

donde isBiggerThan es un método en CustomClass . También podemos usar referencias de métodos aquí:

list.sort(MyClass::isBiggerThan);


Bien, este hilo ya tiene la edad suficiente, por lo que es muy probable que mi respuesta no sea útil para la pregunta. Pero como este hilo me ayudó a encontrar mi solución, lo pondré aquí de todos modos.

Necesitaba usar un método estático variable con entrada conocida y salida conocida (ambas dobles ). Entonces, sabiendo el método y el nombre del método, podría trabajar de la siguiente manera:

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

para una función que acepta un doble como parámetro.

Entonces, en mi situación concreta lo inicialicé con

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

e invocó más tarde en una situación más compleja 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);

Donde la actividad es un doble valor arbitrario. Estoy pensando en hacer esto un poco más abstracto y generalizarlo, como lo ha hecho SoftwareMonkey, pero actualmente estoy bastante contento con la forma en que está. Tres líneas de código, sin clases e interfaces necesarias, eso no es tan malo.


Clase interna anonima

Supongamos que desea que se pase una función con un parámetro String que devuelve un int .
Primero, debe definir una interfaz con la función como su único miembro, si no puede reutilizar una existente.

interface StringFunction { int func(String param); }

Un método que toma el puntero simplemente aceptaría la instancia de StringFunction así:

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

Y se llamaría así:

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

EDITAR: En Java 8, podría llamarlo con una expresión lambda:

ref.takingMethod(param -> bodyExpression);


Cuando hay una cantidad predefinida de cálculos diferentes que puede hacer en esa línea, usar una enumeración es una forma rápida pero clara de implementar un patrón de estrategia.

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

Obviamente, la declaración del método de estrategia, así como exactamente una instancia de cada implementación se definen en una sola clase / archivo.


Debe crear una interfaz que proporcione la (s) función (es) que desea transmitir. p.ej:

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

Luego, cuando necesite pasar una función, puede implementar esa interfaz:

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

Finalmente, la función de mapa usa la función Pasada en 1 como sigue:

public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn, Map<K, S> m1, Map<K, T> m2, Map<K, R> results){ Set<K> keySet = new HashSet<K>(); 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; }

A menudo puede usar Runnable en lugar de su propia interfaz si no necesita pasar parámetros, o puede usar otras técnicas para hacer que el parámetro sea menos "fijo", pero generalmente es una compensación con el tipo de seguridad. (O puede anular el constructor para que su objeto de función pase los parámetros de esa manera ... hay muchos enfoques y algunos funcionan mejor en ciertas circunstancias).


Desde Java8, puede usar lambdas, que también tienen bibliotecas en la API oficial de SE 8.

Uso: Necesita usar una interfaz con un solo método abstracto. Haga una instancia de esto (es posible que desee utilizar una java SE 8 ya provista) de esta manera:

Function<InputType, OutputType> functionname = (inputvariablename) { ... return outputinstance; }

Para obtener más información, consulte la documentación: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html



La respuesta de @ sblundy es excelente, pero las clases internas anónimas tienen dos pequeños defectos, el principal es que tienden a no ser reutilizables y la secundaria es una sintaxis voluminosa.

Lo bueno es que su patrón se expande en clases completas sin ningún cambio en la clase principal (la que realiza los cálculos).

Cuando crea una instancia de una nueva clase, puede pasar parámetros a esa clase que pueden actuar como constantes en su ecuación, por lo que si una de sus clases internas se ve así:

f(x,y)=x*y

Pero a veces necesitas uno que sea:

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

y tal vez un tercero que es:

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

en lugar de crear dos clases internas anónimas o agregar un parámetro "passthrough", puede crear una única clase ACTUAL que cree como:

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

Simplemente almacenaría la constante en la clase y la utilizaría en el método especificado en la interfaz.

De hecho, si SABES que tu función no se almacenará / reutilizará, puedes hacer esto:

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

Pero las clases inmutables son más seguras, no puedo encontrar una justificación para hacer que una clase como esta sea mutable.

Realmente solo publico esto porque me estremezco cuando escucho una clase interna anónima. He visto un montón de código redundante que era "Requerido" porque lo primero que hizo el programador fue ir anónimo cuando debería haber usado una clase real y nunca repensó su decisión.



Ninguna de las respuestas de Java 8 ha dado un ejemplo completo y cohesivo, así que aquí viene.

Declare el método que acepta el "puntero de función" de la siguiente manera:

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

Llámelo proporcionando la función con una expresión lambda:

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


Para cada "puntero de función", crearía una pequeña clase de functor que implementa su cálculo. Defina una interfaz que implementarán todas las clases y pase instancias de esos objetos a su función más grande. Esta es una combinación del " patrón de comando " y el " patrón de estrategia ".

El ejemplo de @ sblundy es bueno.


Para hacer lo mismo sin interfaces para una matriz de funciones:

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) { a.g(x); } }, new NameFuncPair("B") { @Override void f(String x) { b.h(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); } }


Si alguien está luchando para pasar una función que toma un conjunto de parámetros para definir su comportamiento, pero otro conjunto de parámetros en los que ejecutar, como Scheme''s:

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

ver Función de paso con comportamiento de parámetro definido en Java


Si solo tiene una línea que es diferente, puede agregar un parámetro como una bandera y una sentencia if (bandera) que llame a una línea u otra.


Suena como un patrón de estrategia para mí. Echa un vistazo a fluffycat.com patrones de Java.



También puede hacer esto (lo que en algunas ocasiones RARAS tiene sentido). El problema (y es un gran problema) es que se pierden todos los tipos de seguridad en el uso de una clase / interfaz y tiene que lidiar con el caso en el que el método no existe.

Tiene el "beneficio" de que puede ignorar las restricciones de acceso y llamar a métodos privados (no se muestra en el ejemplo, pero puede llamar a métodos a los que el compilador normalmente no le permitiría llamar).

Nuevamente, es raro que esto tenga sentido, pero en esas ocasiones es una buena herramienta para tener.

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


Una de las cosas que realmente extraño cuando programo en Java es la devolución de llamadas de funciones. Una situación en la que la necesidad de estos se presentaba de forma recurrente era el procesamiento recursivo de las jerarquías en las que se desea realizar alguna acción específica para cada elemento. Como recorrer un árbol de directorios o procesar una estructura de datos. El minimalista dentro de mí odia tener que definir una interfaz y luego una implementación para cada caso específico.

Un día me encontré preguntándome por qué no? Tenemos punteros a métodos - el objeto Method. Al optimizar los compiladores JIT, la invocación reflexiva ya no conlleva una gran penalización de rendimiento. Y además, al lado de, por ejemplo, copiar un archivo de una ubicación a otra, el costo de la invocación del método reflejado se vuelve insignificante.

Mientras lo pensaba más, me di cuenta de que una devolución de llamada en el paradigma OOP requiere vincular un objeto y un método juntos: ingrese el objeto Callback.

Echa un vistazo a mi solución basada en la reflexión para Callbacks en Java . Gratis para cualquier uso.


Wow, ¿por qué no crear una clase de Delegado que no es tan difícil dado que ya lo hice para Java y lo uso para pasar el parámetro donde T es el tipo de retorno? Lo siento, pero como programador de C ++ / C # en general, solo estoy aprendiendo java, necesito punteros de función porque son muy útiles. Si está familiarizado con cualquier clase que se ocupe de la información del método, puede hacerlo. En las bibliotecas de java que serían java.lang.reflect.method.

Si siempre usa una interfaz, siempre tiene que implementarla. En la gestión de eventos, realmente no hay una mejor manera de registrar / anular el registro de la lista de manejadores, sino de los delegados a los que necesita pasar funciones y no el tipo de valor, lo que hace que una clase delegada lo maneje para superar las interfaces.


Nuevas interfaces funcionales de Java 8 y referencias de métodos utilizando el operador :: .

Java 8 puede mantener las referencias de los métodos (MyClass :: new) con los punteros " @ Functional Interface ". No es necesario el mismo nombre de método, solo se requiere la misma firma de método.

Ejemplo:

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

Entonces, ¿qué tenemos aquí?

  1. Interfaz funcional: es una interfaz, anotada o no con @FunctionalInterface , que contiene solo una declaración de método.
  2. Referencias de métodos: esto es solo una sintaxis especial, se ve así, objectInstance :: methodName , nada más y nada menos.
  3. Ejemplo de uso: solo un operador de asignación y luego el método de interfaz de llamada.

¡USTED DEBE UTILIZAR INTERFACES FUNCIONALES PARA LISTENERS SOLAMENTE Y SOLO PARA ESO!

Porque todos los demás punteros de función son realmente malos para la legibilidad del código y para la capacidad de comprensión. Sin embargo, las referencias de métodos directos a veces son útiles, por ejemplo, foreach.

Hay varias interfaces funcionales predefinidas:

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

Para versiones anteriores de Java, debe probar Guava Libraries, que tiene una funcionalidad y una sintaxis similares, como Adrian Petrescu ha mencionado anteriormente.

Para una investigación adicional mira en Java 8 Cheatsheet

y gracias a The Guy with The Hat por la especificación del lenguaje Java §15.13 enlace.