Punteros de función en Java

Esto puede ser algo común y trivial, pero parece que tengo problemas para encontrar una respuesta concreta. En C # hay un concepto de delegates, que se relaciona fuertemente con la idea de indicadores de función de C ++. ¿Hay una funcionalidad similar en Java? Dado que los indicadores están algo ausentes, ¿cuál es la mejor manera de hacerlo? Y para ser claros, estamos hablando de primera clase aquí.

La expresión idiomática de Java para funcionalidad de puntero a función es una clase anónima que implementa una interfaz, por ejemplo

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

Actualización: lo anterior es necesario en las versiones de Java anteriores a Java 8. Ahora tenemos alternativas mucho mejores, a saber, lambdas:

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

y referencias de métodos:

 list.sort(MyClass::isGreaterThan); 

Puede sustituir un puntero de función con una interfaz. Supongamos que quiere ejecutar una colección y hacer algo con cada elemento.

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

Esta es la interfaz que podríamos pasar a algunos para decir CollectionUtils2.doFunc (Collection c, IFunction f).

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

Como ejemplo digamos que tenemos una colección de números y le gustaría agregar 1 a cada elemento.

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

Puedes usar la reflexión para hacerlo.

Pase como parámetro el objeto y el nombre del método (como una cadena) y luego invoque el método. Por ejemplo:

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

Y luego usarlo como en:

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

Por supuesto, verifique todas las excepciones y agregue los moldes necesarios.

No, las funciones no son objetos de primera clase en Java. Puede hacer lo mismo implementando una clase de controlador: así se implementan las devoluciones de llamada en el Swing, etc.

Sin embargo, hay propuestas de cierres (el nombre oficial de lo que estás hablando) en futuras versiones de java: Javaworld tiene un artículo interesante.

Esto trae a la mente la Ejecución en el Reino de los Nombres de Steve Yegge. Básicamente, establece que Java necesita un objeto para cada acción y, por lo tanto, no tiene entidades “solo verbales” como indicadores de función.

Para lograr una funcionalidad similar, puede usar clases internas anónimas.

Si tuviera que definir una interfaz Foo :

 interface Foo { Object myFunc(Object arg); } 

Cree una bar método que recibirá un ‘puntero de función’ como argumento:

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

Finalmente llame al método de la siguiente manera:

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

No existe tal cosa en Java. Tendrá que ajustar su función en algún objeto y pasar la referencia a ese objeto para pasar la referencia al método en ese objeto.

Sintácticamente, esto se puede aliviar en cierta medida mediante el uso de clases anónimas definidas en el lugar o clases anónimas definidas como variables miembro de la clase.

Ejemplo:

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

Java8 ha introducido lambdas y referencias de métodos . Por lo tanto, si su función coincide con una interfaz funcional (puede crear la suya propia), puede utilizar una referencia de método en este caso.

Java proporciona un conjunto de interfaces funcionales comunes . mientras que podrías hacer lo siguiente:

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

Implementé el soporte de callback / delegado en Java utilizando la reflexión. Los detalles y la fuente de trabajo están disponibles en mi sitio web .

Cómo funciona

Tenemos una clase principal llamada Callback con una clase anidada llamada WithParms. La API que necesita la callback tomará un objeto Callback como parámetro y, si es necesario, creará una Callback.WithParms como variable de método. Como gran parte de las aplicaciones de este objeto serán recursivas, esto funciona de manera muy limpia.

Dado que el rendimiento sigue siendo una gran prioridad para mí, no quería que se me pidiera crear una matriz de objetos desechables para guardar los parámetros para cada invocación; después de todo, en una gran estructura de datos podría haber miles de elementos, y en un procesamiento de mensajes Es posible que terminemos procesando miles de estructuras de datos por segundo.

Para que sea seguro en cuanto a los hilos, la matriz de parámetros debe existir de forma exclusiva para cada invocación del método de la API, y para la eficacia debe usarse la misma para cada invocación de la callback; Necesitaba un segundo objeto que sería barato de crear para vincular la callback con una matriz de parámetros para la invocación. Pero, en algunos escenarios, el invocador ya tendría una matriz de parámetros por otros motivos. Por estos dos motivos, la matriz de parámetros no pertenecía al objeto Callback. Además, la opción de invocación (que pasa los parámetros como una matriz o como objetos individuales) pertenece a la API mediante la callback, lo que le permite usar la invocación que mejor se adapte a su funcionamiento interno.

La clase anidada WithParms, entonces, es opcional y tiene dos propósitos, contiene la matriz de objetos de parámetro necesaria para las invocaciones de callback, y proporciona 10 métodos invoke () sobrecargados (con de 1 a 10 parámetros) que cargan la matriz de parámetros y luego invocar el objective de callback.

Compruebe los cierres cómo se han implementado en la biblioteca lambdaj. De hecho, tienen un comportamiento muy similar al de los delegates de C #:

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

En relación con la mayoría de las personas aquí, soy nuevo en Java, pero como no he visto una sugerencia similar, tengo otra alternativa que sugerir. No estoy seguro de si es una buena práctica o no, o incluso se sugirió antes y simplemente no lo entendí. Me gusta, ya que creo que es auto descriptivo.

  /*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; 

luego, dependiendo de la aplicación, asignar

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

etc.

y llamar por

  functionpointer.execute();