Java 中的回调函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/443708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:42:45  来源:igfitidea点击:

Callback functions in Java

javacallbackfunction-pointers

提问by Omar Kooheji

Is there a way to pass a call back function in a Java method?

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

The behavior I'm trying to mimic is a .Net Delegate being passed to a function.

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

I've seen people suggesting creating a separate object but that seems overkill, however I am aware that sometimes overkill is the only way to do things.

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

采纳答案by Gant

If you mean somthing like .NET anonymous delegate, I think Java's anonymous class can be used as well.

如果您的意思是 .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));

    }
}

回答by erickson

A method is not (yet) a first-class object in Java; you can't pass a function pointer as a callback. Instead, create an object (which usually implements an interface) that contains the method you need and pass that.

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

Proposals for closures in Java—which would provide the behavior you are looking for—have been made, but none will be included in the upcoming Java 7 release.

已经提出了 Java 中的闭包提案——它将提供您正在寻找的行为——已经提出,但在即将发布的 Java 7 版本中将不包含任何提案。

回答by MattK

When I need this kind of functionality in Java, I usually use the Observer pattern. It does imply an extra object, but I think it's a clean way to go, and is a widely understood pattern, which helps with code readability.

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

回答by Michael Borgwardt

A little nitpicking:

一点吹毛求疵:

I've seem people suggesting creating a separate object but that seems overkill

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

Passing a callback includes creating a separate object in pretty much any OO language, so it can hardly be considered overkill. What you probably mean is that in Java, it requires you to create a separate class, which is more verbose (and more resource-intensive) than in languages with explicit first-class functions or closures. However, anonymous classes at least reduce the verbosity and can be used inline.

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

回答by Mario Fusco

Check the closures how they have been implemented in the lambdaj library. They actually have a behavior very similar to C# delegates:

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

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

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

回答by u404192

I tried using java.lang.reflect to implement 'callback', here's a sample:

我尝试使用 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 ();
    }
}

回答by Peter Wilkinson

I found the idea of implementing using the reflect library interesting and came up with this which I think works quite well. The only down side is losing the compile time check that you are passing valid parameters.

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

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

You use it like this

你像这样使用它

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

回答by monnoo

it's a bit old, but nevertheless... I found the answer of Peter Wilkinson nice except for the fact that it does not work for primitive types like int/Integer. The problem is the .getClass()for the parameters[i], which returns for instance java.lang.Integer, which on the other hand will not be correctly interpreted by getMethod(methodName,parameters[])(Java's fault) ...

它有点旧,但是……我发现 Peter Wilkinson 的答案很好,除了它不适用于 int/Integer 等原始类型。问题是.getClass()for parameters[i],它返回例如java.lang.Integer,另一方面,它不会被getMethod(methodName,parameters[])(Java 的错误)正确解释......

I combined it with the suggestion of Daniel Spiewak (in his answer to this); steps to success included: catching NoSuchMethodException-> getMethods()-> looking for the matching one by method.getName()-> and then explicitly looping through the list of parameters and applying Daniels solution, such identifying the type matches and the signature matches.

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

回答by cprcrack

For simplicity, you can use a Runnable:

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

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

Usage:

用法:

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

回答by Juh_

Since Java 8, there are lambda and method references:

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

For example, if you want a functional interface A -> Bsuch as:

例如,如果您想要一个功能界面,A -> B例如:

import java.util.function.Function;

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

then you can call it like so

那么你可以这样称呼它

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

Also you can pass class method. Say you have:

您也可以传递类方法。说你有:

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

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

Then you can do:

然后你可以这样做:

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

Note:

注意

Here I used the functional interface Function<A,B>, but there are many others in the package java.util.function. Most notable ones are

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

  • Supplier: void -> A
  • Consumer: A -> void
  • BiConsumer: (A,B) -> void
  • Function: A -> B
  • BiFunction: (A,B) -> C
  • Suppliervoid -> A
  • ConsumerA -> void
  • BiConsumer(A,B) -> void
  • FunctionA -> B
  • BiFunction(A,B) -> C

and many others that specialize on some of the input/output type. Then, if it doesn't provide the one you need, you can create your own functional interface like so:

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

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

Example of use:

使用示例:

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

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