196

有没有办法在 Java 方法中传递回调函数?

我试图模仿的行为是将.Net Delegate 传递给函数。

我见过有人建议创建一个单独的对象,但这似乎有点矫枉过正,但是我知道有时矫枉过正是做事的唯一方法。

4

18 回答 18

171

如果您的意思是 .NET 匿名委托之类的东西,我认为 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));

    }
}
于 2009-01-14T16:48:13.247 回答
59

从 Java 8 开始,就有了 lambda 和方法引用:

例如,如果你想要一个函数式接口A -> B,你可以使用:

import java.util.function.Function;

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

你可以这样称呼它:

MyClass.applyFunction("42", str -> "the answer is: " + str);
// returns "the answer is: 42"

你也可以通过类方法。例如:

@Value // lombok
public class PrefixAppender {
    private String prefix;

    public String addPrefix(String suffix){
        return prefix +":"+suffix;
    }
}

然后你可以这样做:

PrefixAppender prefixAppender= new PrefixAppender("prefix");
MyClass.applyFunction("some text", prefixAppender::addPrefix);
// returns "prefix:some text"

注意

这里我使用了功能接口Function<A,B>,但是包里还有很多其他的java.util.function。最值得注意的是

  • Suppliervoid -> A
  • ConsumerA -> void
  • BiConsumer(A,B) -> void
  • FunctionA -> B
  • BiFunction(A,B) -> C

以及许多其他专门研究某些输入/输出类型的人。然后,如果它没有提供您需要的,您可以创建自己的FunctionalInterface

@FunctionalInterface
interface Function3<In1, In2, In3, Out> { // (In1,In2,In3) -> Out
    public Out apply(In1 in1, In2 in2, In3 in3);
}

使用示例:

String computeAnswer(Function3<String, Integer, Integer, String> f){
    return f.apply("6x9=", 6, 9);
}

computeAnswer((question, a, b) -> question + "42");
// "6*9=42"

你也可以通过抛出异常来做到这一点:

@FunctionalInterface
interface FallibleFunction<In, Out, Ex extends Exception> {
    Out get(In input) throws Ex;
}
public <Ex extends IOException> String yo(FallibleFunction<Integer, String, Ex> f) throws Ex {
    return f.get(42);
}
于 2014-11-06T10:02:29.613 回答
39

为简单起见,您可以使用Runnable

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

用法:

runCallback(new Runnable()
{
    @Override
    public void run()
    {
        // Running callback
    }
});
于 2013-12-13T16:41:00.517 回答
26

但是我看到有一种最喜欢的方式就是我一直在寻找的方式。它基本上来自这些答案,但我不得不对其进行操作以使其更加冗余和高效。我认为每个人都在寻找我想出的东西

切中要害::

先做一个这么简单的接口

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

现在让这个回调在你想要处理结果时运行 -更有可能在异步调用之后并且你想要运行一些依赖于这些结果的东西

// 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是需要一些时间的函数,您想向其添加回调以在结果到来时通知您,将回调接口作为参数添加到此方法

希望我的观点很清楚,享受;)

于 2017-12-18T22:47:56.630 回答
17

一点点吹毛求疵:

我似乎有人建议创建一个单独的对象,但这似乎有点矫枉过正

传递回调包括在几乎任何 OO 语言中创建一个单独的对象,因此它几乎不能被认为是矫枉过正。您可能的意思是,在 Java 中,它需要您创建一个单独的类,这比具有显式一等函数或闭包的语言更冗长(并且更耗费资源)。然而,匿名类至少减少了冗长并且可以内联使用。

于 2009-06-22T10:13:57.143 回答
13

这在带有 lambda 的 Java 8 中非常容易。

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();
    }
}
于 2017-04-03T19:27:03.377 回答
7

我发现使用反射库实现的想法很有趣,并想出了这个我认为效果很好的方法。唯一的缺点是失去了您传递有效参数的编译时检查。

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

你像这样使用它

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);
        }
    }
}
于 2011-07-15T04:18:47.600 回答
5

方法(还)不是 Java 中的一等对象;您不能将函数指针作为回调传递。相反,创建一个包含您需要的方法的对象(通常实现一个接口)并传递它。

已经提出了 Java 中的闭包提案——这将提供您正在寻找的行为——但没有一个将包含在即将发布的 Java 7 版本中。

于 2009-01-14T16:48:53.903 回答
4

当我在 Java 中需要这种功能时,我通常使用观察者模式。它确实暗示了一个额外的对象,但我认为这是一种干净的方式,并且是一种广泛理解的模式,有助于提高代码的可读性。

于 2009-01-14T16:50:49.670 回答
4

检查闭包是如何在 lambdaj 库中实现的。它们实际上具有与 C# 委托非常相似的行为:

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

于 2009-09-12T13:32:51.957 回答
3

您也可以Callback使用以下Delegate模式:

回调.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);
    }
}
于 2016-07-06T18:19:27.027 回答
2

我尝试使用 java.lang.reflect 来实现“回调”,这是一个示例:

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 ();
    }
}
于 2011-03-25T04:41:25.530 回答
1

它有点旧,但尽管如此......我发现彼得威尔金森的答案很好,除了它不适用于像 int/Integer 这样的原始类型。问题是.getClass()for parameters[i],它返回例如,另一方面(Java 的错误)java.lang.Integer不会正确解释它......getMethod(methodName,parameters[])

我将它与 Daniel Spiewak 的建议结合起来(在他对此的回答中);成功的步骤包括:捕获NoSuchMethodException-> getMethods()-> 通过 -> 查找匹配method.getName()项,然后显式循环遍历参数列表并应用 Daniels 解决方案,例如识别类型匹配和签名匹配。

于 2011-09-20T20:49:58.697 回答
1

我最近开始做这样的事情:

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);
    }
}
于 2015-10-14T08:03:02.287 回答
1

使用 java 8 这个任务有点简单,如果你想在多线程场景中使用回调,你可以做类似下面的事情:

public  void methodA (int n, IntConsumer consumer) {
    
    // create a thread
    Thread t = new Thread(() -> {
        // some time consuming operation
        int result = IntStream.range(0, n).sum();
        // after the result is ready do something with it.
        consumer.accept(result);
    });
    t.start();
}

并使用此方法:

methodA(1000000, System.out::println);
于 2021-06-02T19:10:00.837 回答
0
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"

基本上如果你想创建一个接口的对象是不可能的,因为接口不能有对象。

选项是让某个类实现接口,然后使用该类的对象调用该函数。但是这种方法真的很冗长。

或者,编写 new HelloWorld() (*oberserve 这是一个接口而不是一个类),然后跟随着接口方法本身的定义。(*这个定义实际上是匿名类)。然后你得到对象引用,通过它你可以调用方法本身。

于 2015-05-01T08:01:54.177 回答
0

创建一个接口,并在回调类中创建相同的接口属性。

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

}

现在在你想要回调的类中实现上面的 Created Interface。并且还传递要回调的类的“this”对象/引用。

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);
    }
}
于 2019-06-10T16:14:12.927 回答
0

最简单和最简单的方法是创建一个可重用的模型和触发器.... https://onecompiler.com/java/3wejrcby2?fbclid=IwAR0dHbGDChRUJoCZ3CIDW-JQu7Dz3iYGNGYjxYVCPCWfEqQDogFGTwuOuO8

于 2020-12-01T23:13:11.910 回答