webentwicklung-frage-antwort-db.com.de

Rückruffunktionen in Java

Gibt es eine Möglichkeit, eine Rückruffunktion in einer Java -Methode zu übergeben?

Das Verhalten, das ich zu imitieren versuche, ist ein .Net-Delegat, der an eine Funktion übergeben wird.

Ich habe schon Leute gesehen, die vorgeschlagen haben, ein separates Objekt zu erstellen, aber das scheint übertrieben zu sein. Ich bin mir jedoch bewusst, dass manchmal Übertreibung die einzige Möglichkeit ist, Dinge zu tun.

161
Omar Kooheji

Wenn Sie so etwas wie einen anonymen .NET-Delegaten meinen, kann auch die anonyme Java-Klasse verwendet werden.

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

    }
}
149
Gant

Seit Java 8 gibt es Lambda und Methodenverweise:

Definieren wir zum Beispiel:

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

und

import Java.util.function.Function;

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

Dann können Sie tun:

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

Ein Beispiel für Github finden Sie hier: julien-diener/MethodReference .

31
Juh_

Der Einfachheit halber können Sie ein Runnable verwenden:

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

Verwendung:

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

Ein kleines Nickerchen:

Ich habe den Anschein, als würden Leute vorschlagen, ein separates Objekt zu erstellen, aber das scheint übertrieben

Das Übergeben eines Rückrufs umfasst das Erstellen eines separaten Objekts in nahezu jeder OO= Sprache, daher kann es kaum als übertrieben angesehen werden. Sie meinen wahrscheinlich, dass Sie in Java eine separate Klasse erstellen müssen, Dies ist ausführlicher (und ressourcenintensiver) als in Sprachen mit expliziten erstklassigen Funktionen oder Abschlüssen, jedoch verringern anonyme Klassen zumindest die Ausführlichkeit und können inline verwendet werden.

16

dennoch sehe ich, dass es den am meisten bevorzugten Weg gibt, nach dem ich gesucht habe. Er leitet sich im Wesentlichen aus diesen Antworten ab, aber ich musste ihn so manipulieren, dass er redundanter und effizienter ist. und ich denke, jeder sucht nach was Ich komme mit

Auf den Punkt gebracht:

erst machen Sie ein Interface so einfach

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

damit dieser Rückruf ausgeführt wird, wann immer Sie möchten, um die Ergebnisse zu verarbeiten - wahrscheinlich nach einem asynchronen Aufruf, und Sie möchten einige Dinge ausführen, die von diesen Wiederverwendungsmöglichkeiten abhängen

// 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 ist die Funktion, die einige Zeit benötigt, um einen Rückruf hinzuzufügen und Sie zu benachrichtigen, wenn die Ergebnisse vorliegen. Fügen Sie dieser Methode die Rückrufschnittstelle als Parameter hinzu

hoffe mein punkt ist klar, viel spaß;)

13

Dies ist in Java 8 mit Lambdas sehr einfach.

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

Ich fand die Idee, mithilfe der reflect-Bibliothek zu implementieren, interessant und kam auf diese Idee, von der ich denke, dass sie recht gut funktioniert. Der einzige Nachteil ist der Verlust der Kompilierungszeit, bei der überprüft wird, ob Sie gültige Parameter übergeben.

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

Du benutzt es so

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

Eine Methode ist in Java (noch) kein erstklassiges Objekt. Sie können einen Funktionszeiger nicht als Rückruf übergeben. Erstellen Sie stattdessen ein Objekt (das normalerweise eine Schnittstelle implementiert), das die von Ihnen benötigte Methode enthält, und übergeben Sie diese.

Vorschläge für Schließungen in Java, die das von Ihnen gesuchte Verhalten liefern würden, wurden gemacht, aber keines wird in der kommenden Version Java 7) enthalten sein.

5
erickson

Wenn ich diese Art von Funktionalität in Java benötige, verwende ich normalerweise das Observer-Muster . Es impliziert ein zusätzliches Objekt, aber ich denke, es ist ein sauberer Weg und ein weithin verstandenes Muster, das die Lesbarkeit des Codes verbessert.

4
MattK

Überprüfen Sie die Verschlüsse, wie sie in der Lambdaj-Bibliothek implementiert wurden. Sie haben tatsächlich ein ähnliches Verhalten wie C # -Delegierte:

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

4
Mario Fusco

Sie können das Callback auch mit dem Delegate Muster ausführen:

Rückruf.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);
    }
}
3
Sébastien

Ich habe versucht, mit Java.lang.reflect 'Callback' zu implementieren. Hier ein Beispiel:

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 ();
    }
}
3
LiuYan 刘研

es ist ein bisschen alt, aber trotzdem ... Ich fand die Antwort von Peter Wilkinson Nice, abgesehen von der Tatsache, dass es nicht für primitive Typen wie int/Integer funktioniert. Das Problem ist die .getClass() für den parameters[i], Die zum Beispiel Java.lang.Integer Zurückgibt, was andererseits von getMethod(methodName,parameters[]) (nicht korrekt interpretiert wird. Javas Schuld) ...

Ich habe es mit dem Vorschlag von Daniel Spiewak kombiniert ( in seiner Antwort darauf ); Zu den Schritten zum Erfolg gehörten: NoSuchMethodException -> getMethods() -> Suchen nach dem passenden durch method.getName() -> und dann explizit die Liste der Parameter durchlaufen und Daniels anwenden Lösung, wie das Identifizieren der Typübereinstimmungen und der Signaturübereinstimmungen.

1
monnoo

Ich habe kürzlich angefangen, so etwas zu tun:

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

Grundsätzlich ist es nicht möglich, das Objekt einer Schnittstelle zu erstellen, da die Schnittstelle keine Objekte haben kann.

Sie können die Schnittstelle von einer Klasse implementieren lassen und diese Funktion dann mit dem Objekt dieser Klasse aufrufen. Aber dieser Ansatz ist wirklich wortreich.

Schreiben Sie alternativ new HelloWorld () (* beachten Sie, dass dies eine Schnittstelle und keine Klasse ist) und definieren Sie anschließend die Schnittstellenmethoden selbst. (* Diese Definition ist in Wirklichkeit die anonyme Klasse). Dann erhalten Sie die Objektreferenz, über die Sie die Methode selbst aufrufen können.

0

Erstellen Sie eine Schnittstelle und erstellen Sie dieselbe Schnittstelleneigenschaft in der Rückrufklasse.

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

}

Implementieren Sie jetzt in der Klasse, in der Sie zurückgerufen werden möchten, das oben angegebene Created Interface. Übergeben Sie auch "dieses" Objekt/die Referenz Ihrer Klasse, um zurückgerufen zu werden.

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);
    }
}
0
Balu mallisetty

Ich finde es eleganter, eine abstrakte Klasse zu verwenden:

// 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
*/
0
avatar1337