Question

Y a-t-il un moyen de passer une fonction de rappel dans une méthode Java?

Le comportement que j'essaie d'imiter est un délégué .Net transmis à une fonction.

J'ai déjà vu des personnes suggérer de créer un objet séparé, mais cela semble excessif, mais je suis conscient que parfois, il est parfois excessif d'exécuter des tâches.

Était-ce utile?

La solution

Si vous voulez dire quelque chose comme un délégué anonyme .NET, je pense que la classe anonyme Java peut également être utilisée.

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

    }
}

Autres conseils

Depuis Java 8, il existe des références à lambda et à une méthode:

Par exemple, définissons:

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

et

import java.util.function.Function;

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

Ensuite, vous pouvez faire:

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

Vous trouverez un exemple sur github ici: julien-diener / MethodReference .

Par souci de simplicité, vous pouvez utiliser un Runnable :

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

Utilisation:

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

Un peu piquant:

  

J'ai l'impression que des gens suggèrent de créer un   objet séparé mais cela semble   surpuissance

Passer un rappel implique de créer un objet séparé dans à peu près n'importe quel langage OO, de sorte qu'il peut difficilement être considéré comme excessif. Ce que vous voulez probablement dire, c'est qu'en Java, il est nécessaire de créer une classe séparée, plus détaillée (et plus gourmande en ressources) que dans les langages dotés de fonctions ou de fermetures explicites de première classe. Cependant, les classes anonymes réduisent au moins la verbosité et peuvent être utilisées en ligne.

Pourtant, je vois qu’il existe un moyen privilégié qui correspond à ce que je cherchais. Il est essentiellement dérivé de ces réponses, mais j’ai dû le manipuler pour le rendre plus redondant et efficace. et je pense que tout le monde cherche ce que je viens avec

Au point ::

commencez par créer une interface aussi simple

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

maintenant de faire en sorte que ce rappel soit exécuté chaque fois que vous souhaitez traiter les résultats - plus probablement après un appel asynchrone et si vous souhaitez exécuter des tâches qui dépendent de ces références

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

}

doQuelque chose est la fonction qui prend un certain temps pour lui ajouter un rappel afin de vous avertir de la réception des résultats. Ajoutez l'interface de rappel en tant que paramètre de cette méthode

espérons que mon point est clair, profitez-en;)

C’est très facile avec Java 8 avec 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();
    }
}

J'ai trouvé l’idée de mettre en œuvre l’utilisation de la bibliothèque de réflexion intéressante et j’ai proposé ce qui, à mon avis, fonctionne assez bien. Le seul inconvénient est de perdre la vérification de la compilation lors de la compilation de paramètres valides.

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

Vous l'utilisez comme ceci

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

Une méthode n'est pas (encore) un objet de première classe en Java; vous ne pouvez pas transmettre un pointeur de fonction en tant que rappel. Créez plutôt un objet (qui implémente généralement une interface) contenant la méthode dont vous avez besoin et transmettez-le.

Des propositions de fermetures en Java offrant le comportement que vous recherchez & # 8212; ont été formulées, mais aucune ne sera incluse dans la prochaine version de Java 7.

Lorsque j'ai besoin de ce type de fonctionnalité en Java, j'utilise généralement le Modèle Observer . Cela implique un objet supplémentaire, mais je pense que c’est une façon propre d’aller. Il s’agit d’un motif largement compris, qui facilite la lisibilité du code.

Vérifiez les fermetures de la manière dont elles ont été implémentées dans la bibliothèque lambdaj. Ils ont en fait un comportement très similaire à celui des délégués C #:

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

J'ai essayé d'utiliser java.lang.reflect pour implémenter 'callback', voici un exemple:

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

Vous pouvez également effectuer le Callback à l'aide du modèle 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);
    }
}

J'ai récemment commencé à faire quelque chose comme ceci:

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

c'est un peu vieux, mais néanmoins ... J'ai trouvé la réponse de Peter Wilkinson agréable sauf que cela ne fonctionne pas pour les types primitifs comme int / Integer. Le problème est le .getClass () pour les paramètres [i] , qui renvoie par exemple java.lang.Integer , ce qui, d'autre main ne sera pas correctement interprétée par getMethod (methodName, parameters []) (faute de Java) ...

Je l'ai combiné avec la suggestion de Daniel Spiewak ( dans sa réponse à cela ); étapes vers le succès inclus: attraper NoSuchMethodException - > getMethods () - > rechercher le correspondant par method.getName () - > puis explicitement en parcourant la liste des paramètres et en appliquant la solution Daniels, en identifiant les correspondances de type et les correspondances de signature.

Je pense que l'utilisation d'une classe abstraite est plus élégante, comme ceci:

// 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"

En gros si vous voulez faire l'objet d'une interface c'est impossible, car l'interface ne peut pas avoir d'objets.

L'option consiste à laisser une classe implémenter l'interface, puis appeler cette fonction à l'aide de l'objet de cette classe. Mais cette approche est vraiment verbeuse.

Sinon, écrivez new HelloWorld () (* oberserve c'est une interface et non une classe) puis suivez-le avec la définition des méthodes d'interface elles-mêmes. (* Cette définition est en réalité la classe anonyme). Ensuite, vous obtenez la référence d'objet à travers laquelle vous pouvez appeler la méthode elle-même.

Créer une interface et créer la même propriété d'interface dans la classe de rappel.

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

}

Maintenant, dans la classe où vous voulez être rappelé, implémentez l'interface créée ci-dessus. et passez également " this " Objet / Référence de votre classe à rappeler.

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);
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top