Pregunta

¿Hay alguna forma de pasar una función de devolución de llamada en un método Java?

El comportamiento que estoy tratando de imitar es un delegado .Net que se pasa a una función.

He visto personas sugiriendo crear un objeto separado, pero eso parece excesivo, sin embargo, soy consciente de que a veces el exceso es la única forma de hacer las cosas.

¿Fue útil?

Solución

Si te refieres a algo como delegado anónimo .NET, creo que también se puede usar la clase anónima Java.

public class Main {

    public interface Visitor{
        int doJob(int a, int b);
    }


    public static void main(String[] args) {
        Visitor adder = new Visitor(){
            public int doJob(int a, int b) {
                return a + b;
            }
        };

        Visitor multiplier = new Visitor(){
            public int doJob(int a, int b) {
                return a*b;
            }
        };

        System.out.println(adder.doJob(10, 20));
        System.out.println(multiplier.doJob(10, 20));

    }
}

Otros consejos

Desde Java 8, hay referencias lambda y de métodos:

Por ejemplo, definamos:

public class FirstClass {
    String prefix;
    public FirstClass(String prefix){
        this.prefix = prefix;
    }
    public String addPrefix(String suffix){
        return prefix +":"+suffix;
    }
}

y

import java.util.function.Function;

public class SecondClass {
    public String applyFunction(String name, Function<String,String> function){
        return function.apply(name);
    }
}

Entonces puedes hacer:

FirstClass first = new FirstClass("first");
SecondClass second = new SecondClass();
System.out.println(second.applyFunction("second",first::addPrefix));

Puede encontrar un ejemplo en github, aquí: julien-diener / MethodReference .

Para simplificar, puede usar un Ejecutable :

private void runCallback(Runnable callback)
{
    // Run callback
    callback.run();
}

Uso:

runCallback(new Runnable()
{
    @Override
    public void run()
    {
        // Running callback
    }
});

Un pequeño truco:

  

Parece que la gente sugiere crear un   objeto separado pero eso parece   exagerar

Pasar una devolución de llamada incluye crear un objeto separado en casi cualquier lenguaje OO, por lo que difícilmente se puede considerar excesivo. Lo que probablemente quiere decir es que en Java, requiere que cree una clase separada, que es más detallada (y requiere más recursos) que en lenguajes con funciones o cierres explícitos de primera clase. Sin embargo, las clases anónimas al menos reducen la verbosidad y se pueden usar en línea.

sin embargo, veo que es la forma más preferida, que era lo que estaba buscando ... básicamente se deriva de estas respuestas, pero tuve que manipularlo para que sea más redundante y eficiente ... y creo que todos buscan qué se me ocurrió

Hasta el punto ::

primero haga una interfaz así de simple

public interface myCallback {
    void onSuccess();
    void onError(String err);
}

ahora para hacer que esta devolución de llamada se ejecute siempre que desee hacer para manejar los resultados - es más probable después de una llamada asincrónica y desea ejecutar algunas cosas que dependen de estas reutilizaciones

// import the Interface class here

public class App {

    public static void main(String[] args) {
        // call your method
        doSomething("list your Params", new myCallback(){
            @Override
            public void onSuccess() {
                // no errors
                System.out.println("Done");
            }

            @Override
            public void onError(String err) {
                // error happen
                System.out.println(err);
            }
        });
    }

    private void doSomething(String param, // some params..
                             myCallback callback) {
        // now call onSuccess whenever you want if results are ready
        if(results_success)
            callback.onSuccess();
        else
            callback.onError(someError);
    }

}

doSomething es la función que lleva algún tiempo que desee agregarle una devolución de llamada para notificarle cuándo llegaron los resultados, agregue la interfaz de devolución de llamada como parámetro de este método

espero que mi punto sea claro, disfruta;)

Esto es muy fácil en Java 8 con lambdas.

public interface Callback {
    void callback();
}

public class Main {
    public static void main(String[] args) {
        methodThatExpectsACallback(() -> System.out.println("I am the callback."));
    }
    private static void methodThatExpectsACallback(Callback callback){
        System.out.println("I am the method.");
        callback.callback();
    }
}

Me pareció interesante la idea de implementar el uso de la biblioteca reflect y se me ocurrió esto, que creo que funciona bastante bien. El único inconveniente es perder el tiempo de compilación y comprobar que está pasando parámetros válidos.

public class CallBack {
    private String methodName;
    private Object scope;

    public CallBack(Object scope, String methodName) {
        this.methodName = methodName;
        this.scope = scope;
    }

    public Object invoke(Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Method method = scope.getClass().getMethod(methodName, getParameterClasses(parameters));
        return method.invoke(scope, parameters);
    }

    private Class[] getParameterClasses(Object... parameters) {
        Class[] classes = new Class[parameters.length];
        for (int i=0; i < classes.length; i++) {
            classes[i] = parameters[i].getClass();
        }
        return classes;
    }
}

Lo usas así

public class CallBackTest {
    @Test
    public void testCallBack() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        TestClass testClass = new TestClass();
        CallBack callBack = new CallBack(testClass, "hello");
        callBack.invoke();
        callBack.invoke("Fred");
    }

    public class TestClass {
        public void hello() {
            System.out.println("Hello World");
        }

        public void hello(String name) {
            System.out.println("Hello " + name);
        }
    }
}

Un método no es (todavía) un objeto de primera clase en Java; no puede pasar un puntero de función como devolución de llamada. En su lugar, cree un objeto (que generalmente implementa una interfaz) que contenga el método que necesita y páselo.

Se han realizado propuestas para cierres en Java & # 8212; que proporcionarían el comportamiento que está buscando & # 8212; pero ninguno se incluirá en la próxima versión de Java 7.

Cuando necesito este tipo de funcionalidad en Java, generalmente uso el Patrón de observador . Sí implica un objeto adicional, pero creo que es un camino limpio, y es un patrón ampliamente entendido, que ayuda con la legibilidad del código.

Verifique los cierres cómo se han implementado en la biblioteca lambdaj. Realmente tienen un comportamiento muy similar al de los delegados de C #:

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

Intenté usar java.lang.reflect para implementar 'callback', aquí hay una muestra:

package StackOverflowQ443708_JavaCallBackTest;

import java.lang.reflect.*;
import java.util.concurrent.*;

class MyTimer
{
    ExecutorService EXE =
        //Executors.newCachedThreadPool ();
        Executors.newSingleThreadExecutor ();

    public static void PrintLine ()
    {
        System.out.println ("--------------------------------------------------------------------------------");
    }

    public void SetTimer (final int timeout, final Object obj, final String methodName, final Object... args)
    {
        SetTimer (timeout, obj, false, methodName, args);
    }
    public void SetTimer (final int timeout, final Object obj, final boolean isStatic, final String methodName, final Object... args)
    {
        Class<?>[] argTypes = null;
        if (args != null)
        {
            argTypes = new Class<?> [args.length];
            for (int i=0; i<args.length; i++)
            {
                argTypes[i] = args[i].getClass ();
            }
        }

        SetTimer (timeout, obj, isStatic, methodName, argTypes, args);
    }
    public void SetTimer (final int timeout, final Object obj, final String methodName, final Class<?>[] argTypes, final Object... args)
    {
        SetTimer (timeout, obj, false, methodName, argTypes, args);
    }
    public void SetTimer (final int timeout, final Object obj, final boolean isStatic, final String methodName, final Class<?>[] argTypes, final Object... args)
    {
        EXE.execute (
            new Runnable()
            {
                public void run ()
                {
                    Class<?> c;
                    Method method;
                    try
                    {
                        if (isStatic) c = (Class<?>)obj;
                        else c = obj.getClass ();

                        System.out.println ("Wait for " + timeout + " seconds to invoke " + c.getSimpleName () + "::[" + methodName + "]");
                        TimeUnit.SECONDS.sleep (timeout);
                        System.out.println ();
                        System.out.println ("invoking " + c.getSimpleName () + "::[" + methodName + "]...");
                        PrintLine ();
                        method = c.getDeclaredMethod (methodName, argTypes);
                        method.invoke (obj, args);
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        PrintLine ();
                    }
                }
            }
        );
    }
    public void ShutdownTimer ()
    {
        EXE.shutdown ();
    }
}

public class CallBackTest
{
    public void onUserTimeout ()
    {
        System.out.println ("onUserTimeout");
    }
    public void onTestEnd ()
    {
        System.out.println ("onTestEnd");
    }
    public void NullParameterTest (String sParam, int iParam)
    {
        System.out.println ("NullParameterTest: String parameter=" + sParam + ", int parameter=" + iParam);
    }
    public static void main (String[] args)
    {
        CallBackTest test = new CallBackTest ();
        MyTimer timer = new MyTimer ();

        timer.SetTimer ((int)(Math.random ()*10), test, "onUserTimeout");
        timer.SetTimer ((int)(Math.random ()*10), test, "onTestEnd");
        timer.SetTimer ((int)(Math.random ()*10), test, "A-Method-Which-Is-Not-Exists");    // java.lang.NoSuchMethodException

        timer.SetTimer ((int)(Math.random ()*10), System.out, "println", "this is an argument of System.out.println() which is called by timer");
        timer.SetTimer ((int)(Math.random ()*10), System.class, true, "currentTimeMillis");
        timer.SetTimer ((int)(Math.random ()*10), System.class, true, "currentTimeMillis", "Should-Not-Pass-Arguments");    // java.lang.NoSuchMethodException

        timer.SetTimer ((int)(Math.random ()*10), String.class, true, "format", "%d %X", 100, 200); // java.lang.NoSuchMethodException
        timer.SetTimer ((int)(Math.random ()*10), String.class, true, "format", "%d %X", new Object[]{100, 200});

        timer.SetTimer ((int)(Math.random ()*10), test, "NullParameterTest", new Class<?>[]{String.class, int.class}, null, 888);

        timer.ShutdownTimer ();
    }
}

También puede hacer la Devolución de llamada usando el patrón Delegate :

Callback.java

public interface Callback {
    void onItemSelected(int position);
}

PagerActivity.java

public class PagerActivity implements Callback {

    CustomPagerAdapter mPagerAdapter;

    public PagerActivity() {
        mPagerAdapter = new CustomPagerAdapter(this);
    }

    @Override
    public void onItemSelected(int position) {
        // Do something
        System.out.println("Item " + postion + " selected")
    }
}

CustomPagerAdapter.java

public class CustomPagerAdapter {
    private static final int DEFAULT_POSITION = 1;
    public CustomPagerAdapter(Callback callback) {
        callback.onItemSelected(DEFAULT_POSITION);
    }
}

Recientemente comencé a hacer algo como esto:

public class Main {
    @FunctionalInterface
    public interface NotDotNetDelegate {
        int doSomething(int a, int b);
    }

    public static void main(String[] args) {
        // in java 8 (lambdas):
        System.out.println(functionThatTakesDelegate((a, b) -> {return a*b;} , 10, 20));

    }

    public static int functionThatTakesDelegate(NotDotNetDelegate del, int a, int b) {
        // ...
        return del.doSomething(a, b);
    }
}

es un poco viejo, pero sin embargo ... la respuesta de Peter Wilkinson me pareció agradable, excepto por el hecho de que no funciona para tipos primitivos como int / Integer. El problema es el .getClass () para los parámetros [i] , que devuelve, por ejemplo, java.lang.Integer , que por otro getMethod (methodName, parameter []) (error de Java) ...

no interpretará correctamente la mano.

Lo combiné con la sugerencia de Daniel Spiewak ( en su respuesta a esto ); los pasos para el éxito incluyeron: capturar NoSuchMethodException - > getMethods () - > buscando el coincidente por method.getName () - > y luego recorrer explícitamente la lista de parámetros y aplicar la solución Daniels, como identificar las coincidencias de tipo y las coincidencias de firma.

Creo que usar una clase abstracta es más elegante, así:

// Something.java

public abstract class Something {   
    public abstract void test();        
    public void usingCallback() {
        System.out.println("This is before callback method");
        test();
        System.out.println("This is after callback method");
    }
}

// CallbackTest.java

public class CallbackTest extends Something {
    @Override
    public void test() {
        System.out.println("This is inside CallbackTest!");
    }

    public static void main(String[] args) {
        CallbackTest myTest = new CallbackTest();
        myTest.usingCallback();
    }    
}

/*
Output:
This is before callback method
This is inside CallbackTest!
This is after callback method
*/
public class HelloWorldAnonymousClasses {

    //this is an interface with only one method
    interface HelloWorld {
        public void printSomething(String something);
    }

    //this is a simple function called from main()
    public void sayHello() {

    //this is an object with interface reference followed by the definition of the interface itself

        new HelloWorld() {
            public void printSomething(String something) {
                System.out.println("Hello " + something);
            }
        }.printSomething("Abhi");

     //imagine this as an object which is calling the function'printSomething()"
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
                new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }
}
//Output is "Hello Abhi"

Básicamente, si desea hacer que el objeto de una interfaz sea no es posible, porque la interfaz no puede tener objetos.

La opción es permitir que alguna clase implemente la interfaz y luego llamar a esa función usando el objeto de esa clase. Pero este enfoque es realmente detallado.

Alternativamente, escriba un nuevo HelloWorld () (* observe que esta es una interfaz, no una clase) y luego siga con la definición de los métodos de la interfaz. (* Esta definición es en realidad la clase anónima). Luego obtienes la referencia del objeto a través del cual puedes llamar al método en sí.

Cree una interfaz y cree la misma propiedad de interfaz en la clase de devolución de llamada.

interface dataFetchDelegate {
    void didFetchdata(String data);
}
//callback class
public class BackendManager{
   public dataFetchDelegate Delegate;

   public void getData() {
       //Do something, Http calls/ Any other work
       Delegate.didFetchdata("this is callbackdata");
   }

}

Ahora, en la clase en la que desea que se le llame, implemente la interfaz creada anteriormente. y también Pase " esto " Objeto / referencia de su clase para ser devuelto.

public class Main implements dataFetchDelegate
{       
    public static void main( String[] args )
    {
        new Main().getDatafromBackend();
    }

    public void getDatafromBackend() {
        BackendManager inc = new BackendManager();
        //Pass this object as reference.in this Scenario this is Main Object            
        inc.Delegate = this;
        //make call
        inc.getData();
    }

    //This method is called after task/Code Completion
    public void didFetchdata(String callbackData) {
        // TODO Auto-generated method stub
        System.out.println(callbackData);
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top