Funkcje zwrotne w Javie

Czy istnieje sposób na przekazanie funkcji call back w metodzie Java?

Zachowanie, które próbuję naśladować, to Delegat. Net przekazywany do funkcji.

Widziałem ludzi sugerujących stworzenie osobnego obiektu, ale wydaje się to przesadą, jednak zdaję sobie sprawę, że czasami przesada jest jedynym sposobem na robienie rzeczy.

Author: Ozair Kafray, 2009-01-14

16 answers

Jeśli masz na myśli coś takiego jak. NET anonymous delegate, myślę, że można również użyć anonimowej klasy Javy.

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

    }
}
 137
Author: Gant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-17 12:59:31

Od Javy 8 istnieją odwołania do lambda i metody:

Na przykład, zdefiniujmy:

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

I

import java.util.function.Function;

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

Wtedy możesz zrobić:

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

Możesz znaleźć przykład na GitHubie, tutaj: julien-diener / MethodReference .

 28
Author: Juh_,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-29 20:30:00

Dla uproszczenia, możesz użyć Runnable :

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

Użycie:

runCallback(new Runnable()
{
    @Override
    public void run()
    {
        // Running callback
    }
});
 23
Author: cprcrack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-13 16:41:00

Trochę nitpicking:

Wydaje mi się, że ludzie sugerują stworzenie osobny obiekt, ale wydaje się, że overkill

Przekazywanie wywołania zwrotnego obejmuje tworzenie osobnego obiektu w prawie każdym języku OO, więc trudno go uznać za przesadę. Prawdopodobnie chodzi Ci o to, że w Javie wymaga to utworzenia osobnej klasy, która jest bardziej wyrazista (i bardziej zasobochłonna) niż w językach z jawnymi funkcjami pierwszej klasy lub zamknięciami. Jednak anonimowy klasy co najmniej zmniejszają słowność i mogą być używane w linii.

 16
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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-22 10:13:57

Jednak widzę, że jest najbardziej preferowany sposób, który był tym, czego szukałem.. to w zasadzie pochodzi z tych odpowiedzi, ale musiałem manipulować nim, aby był bardziej zbędny i wydajny.. i myślę, że każdy szuka tego, co wymyślę

Do punktu:

Najpierw stwórz interfejs tak prosty

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

Teraz, aby to callback uruchomić, gdy kiedykolwiek chcesz zrobić, aby obsłużyć wyniki - bardziej prawdopodobne po asynchronicznym wywołaniu i chcesz uruchomić kilka rzeczy, które zależą od tych reusltów

// 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 czy funkcja, która zajmuje trochę czasu, chcesz dodać do niej callback, aby powiadomić cię, gdy pojawiły się wyniki, Dodaj interfejs oddzwaniania jako parametr do tej metody

Mam nadzieję, że mój punkt widzenia jest jasny, enjoy;)

 10
Author: Biskrem Muhammad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 08:47:52

Pomysł wdrożenia za pomocą biblioteki reflect był dla mnie interesujący i wpadł na to pomysł, który moim zdaniem działa całkiem dobrze. Jedyną wadą jest utrata sprawdzania czasu kompilacji, czy przekazujesz poprawne parametry.

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

Używasz go w ten sposób

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
Author: Peter Wilkinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-15 04:18:47

Metoda nie jest (jeszcze) obiektem pierwszej klasy w Javie; nie można przekazać wskaźnika funkcji jako wywołania zwrotnego. Zamiast tego utwórz obiekt (który zazwyczaj implementuje interfejs) zawierający potrzebną metodę i przekaż ją.

Propozycje zamknięć w Javie-które zapewniłyby zachowanie, którego szukasz-zostały złożone, ale żadna z nich nie zostanie uwzględniona w nadchodzącym wydaniu Javy 7.

 5
Author: erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-14 16:48:53

Kiedy potrzebuję tego rodzaju funkcjonalności w Javie, zwykle używam wzorca obserwatora . Oznacza to dodatkowy obiekt, ale myślę, że jest to czysty sposób, i jest szeroko rozumianym wzorem, który pomaga w czytelności kodu.

 4
Author: MattK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-14 16:50:49

Sprawdź jak zostały one zaimplementowane w bibliotece lambdaj. W rzeczywistości mają zachowanie bardzo podobne do C# delegatów:

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

 4
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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 13:32:51

Jest to bardzo proste w Javie 8 z lambda.

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();
    }
}
 4
Author: Uncle Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-03 19:27:03

Próbowałem używać Javy.lang.zastanów się, aby zaimplementować 'callback', oto przykład:

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
Author: LiuYan 刘研,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-17 18:54:51

Możesz również wykonać {[3] } używając wzoru Delegate:

/ Align = "left" / 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
Author: Sébastien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-06 20:47:24

Jest trochę stary, ale mimo to... Znalazłem odpowiedź Peter Wilkinson ładny z wyjątkiem faktu, że to nie działa dla prymitywnych typów, takich jak int / Integer. Problemem jest .getClass() dla parameters[i], który zwraca na przykład java.lang.Integer, który z drugiej strony nie będzie poprawnie interpretowany przez getMethod(methodName,parameters[]) (Błąd Javy) ...

Połączyłem to z sugestią Daniela śpiewaka (w jego odpowiedzi na to ); kroki do sukcesu obejmowały: łapanie NoSuchMethodException -> getMethods() -> Szukam dopasowanie jednego przez method.getName() ->, a następnie jawne zapętlenie listy parametrów i zastosowanie rozwiązania Danielsa, np. określenie dopasowania typu i dopasowania podpisu.

 1
Author: monnoo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:42

Myślę, że używanie klasy abstrakcyjnej jest bardziej eleganckie, jak to:

// 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
Author: avatar1337,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-20 23:44:49

Ostatnio zacząłem robić coś takiego:

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);
    }
}
 0
Author: joey baruch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-14 08:03:02
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"

W zasadzie Jeśli chcesz, aby obiekt interfejsu był niemożliwe, ponieważ interfejs nie może zawierać obiektów.

Opcja jest taka, aby jakaś Klasa zaimplementowała interfejs, a następnie wywołała tę funkcję używając obiektu tej klasy. Ale to podejście jest naprawdę gadatliwe.

Alternatywnie, napisz new HelloWorld () (*oberserve to jest interfejs, a nie klasa), a następnie wykonaj go z definacją samych metod interfejsu. (*Ta definacja jest w rzeczywistości Klasa anonimowa). Następnie otrzymasz odniesienie do obiektu, za pomocą którego możesz wywołać samą metodę.

 0
Author: Abhishek Abhyankar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-20 02:47:17