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 delegados, que se relaciona fuertemente con la idea de punteros 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.

Author: Ben Lakey, 2009-07-02

11 answers

El lenguaje Java para la funcionalidad tipo puntero de función es una clase anónima que implementa una interfaz, por ejemplo,

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

Update: lo anterior es necesario en versiones de Java anteriores a Java 8. Ahora tenemos alternativas mucho más agradables, a saber lambdas:

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

Y referencias metodológicas:

list.sort(MyClass::isGreaterThan);
 117
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-23 09:48:14

Puede sustituir un puntero de función por una interfaz. Digamos que quieres recorrer 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 dicen CollectionUtils2.doFunc(Colección 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++;
    }
});
 62
Author: raupach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-02 09:38:12

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, marque todas las excepciones y agregue los moldes necesarios.

 41
Author: zoquete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-12 13:56:36

No, las funciones no son objetos de primera clase en java. Puedes hacer lo mismo implementando una clase handler - así es como 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.

 20
Author: jwoolard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-02 10:47:22

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

 13
Author: Yuval F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-14 13:40:12

Para lograr una funcionalidad similar, podría usar clases internas anónimas.

Si tuviera que definir una interfaz Foo:

interface Foo {
    Object myFunc(Object arg);
}

Cree un método bar 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.
    }
}
 6
Author: Kingamajick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-02 11:03:21

No existe tal cosa en Java. Necesitará envolver 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 facilitar hasta cierto punto 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");
        }
    }
}
 5
Author: VoidPointer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-04 19:25:14

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 usar una referencia de método en este caso.

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

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}
 5
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-20 11:23:29

He implementado el soporte callback/delegate en Java usando reflection. Los detalles y la fuente de trabajo están disponibles en mi sitio web.

Cómo Funciona

Tenemos una clase principle llamada Callback con una clase anidada llamada WithParms. La API que necesita la devolución de llamada tomará un objeto de devolución de llamada como parámetro y, si es necesario, creará una devolución de llamada.WithParms como variable de método. Dado que muchas de las aplicaciones de este objeto serán recursivas, esto funciona muy limpio.

Con el rendimiento todavía una alta prioridad para mí, no quería que se me pidiera crear una matriz de objetos desechables para contener los parámetros para cada invocación - después de todo, en una estructura de datos grande podría haber miles de elementos, y en un escenario de procesamiento de mensajes podríamos terminar procesando miles de estructuras de datos por segundo.

Para ser threadsafe, la matriz de parámetros debe existir de forma única para cada invocación del método API, y para la eficiencia el mismo debería ser usado para cada invocación de la devolución de llamada; necesitaba un segundo objeto que sería barato de crear para enlazar la devolución de llamada 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 otras razones. Por estas dos razones, el array de parámetros no pertenecía al objeto Callback. También la elección de la invocación (pasando los parámetros como una matriz o como objetos individuales) pertenece a las manos de la API usando la devolución de llamada que le permite usar la invocación que mejor se adapte a su funcionamiento interno.

La clase anidada WithParms, entonces, es opcional y sirve para dos propósitos, contiene la matriz de objetos de parámetros necesaria para las invocaciones de devolución de llamada, y proporciona 10 métodos invoke() sobrecargados (con de 1 a 10 parámetros) que cargan la matriz de parámetros y luego invocan el destino de devolución de llamada.

 3
Author: Lawrence Dol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-16 00:42:04

Compruebe los cierres cómo se han implementado en la biblioteca lambdaj. En realidad tienen un comportamiento muy similar a los delegados de C#:

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

 2
Author: Mario Fusco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-12 13:23:44

En relación con la mayoría de la gente 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 sugerido antes y simplemente no lo entendí. Me gusta porque 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, asigne

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

Etc.

Y llamada de

 functionpointer.execute();
 0
Author: Özgen Eren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-10 11:38:56