java回调函数问题?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java回调函数问题?相关的知识,希望对你有一定的参考价值。

A类里有一个a方法,B类丽有一个b方法,调用b方法时,调用A类里的a方法,当A类调用b方法时,a就是回调函数。我不知道我理解的对吗!这样有什么意义。自己调用a方法不好吗?

下面使用java回调函数来实现一个测试函数运行时间的工具类:  如果我们要测试一个类的方法的执行时间,通常我们会这样做:  java 代码  public class TestObject   /**  * 一个用来被测试的方法,进行了一个比较耗时的循环  */  public static void testMethod()  for ( int i= 0 ; i< 100000000 ; i++)      /**  * 一个简单的测试方法执行时间的方法  */  public void testTime()  long begin = System.currentTimeMillis(); //测试起始时间  testMethod(); //测试方法  long end = System.currentTimeMillis(); //测试结束时间  System.out.println("[use time]:" + (end - begin)); //打印使用时间    public static void main(String[] args)   TestObject test=new TestObject();  test.testTime();      大家看到了testTime()方法,就只有"//测试方法"是需要改变的,下面我们来做一个函数实现相同功能但更灵活:  首先定一个回调接口:  java 代码  public interface CallBack   //执行回调操作的方法  void execute();    然后再写一个工具类:  java 代码  public class Tools   /**  * 测试函数使用时间,通过定义CallBack接口的execute方法  * @param callBack  */  public void testTime(CallBack callBack)   long begin = System.currentTimeMillis(); //测试起始时间  callBack.execute(); ///进行回调操作  long end = System.currentTimeMillis(); //测试结束时间  System.out.println("[use time]:" + (end - begin)); //打印使用时间    public static void main(String[] args)   Tools tool = new Tools();  tool.testTime(new CallBack()  //定义execute方法  public void execute()  //这里可以加放一个或多个要测试运行时间的方法  TestObject.testMethod();    );      大家看到,testTime()传入定义callback接口的execute()方法就可以实现回调功能 参考技术A 回调是一种双向调用模式,被调用方在接口被调用时也会调用对方的接口,
即A调用B中的方法x去处理问题,处理完成后,B又反过来调用A的方法y,此时y即为回调函数
参考技术B 也算把,回调函数是调用者调用某一个方法的时候,那个方法返回处理的结果给调用者的回调函数!这是我的理解!回调函数在前端的开发很常用到,主要是ajax,调用后台的方法,后台把结果返回给前台,前台然后刷新页面! 参考技术C 可能你调用b方法时改变了整个程序的一些状态,回调a方法时,可能需要把变化的内容更新给用户。这可能只是众多情况中的一种而已。

Java中的回调函数

【中文标题】Java中的回调函数【英文标题】:Callback functions in Java 【发布时间】:2010-10-01 10:01:59 【问题描述】:

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

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

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

【问题讨论】:

这有点矫枉过正,因为 Java 没有功能(这应该是双关语......)。 另一个 java 8 示例:***.com/questions/14319787/… 【参考方案1】:

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

    

【讨论】:

这是自 Java 1.0 以来的规范方法。 我一直在使用这个,它比我想要的更冗长,但它有效。 @Omar,同意。在长期使用 C# 之后,我又回到了 Java,真的很想念 lambdas/delegates。来吧Java! @DrewNoakes,好消息是,Java 8 有 lambdas(大部分)... 由于您已经使用单个方法定义了访问者接口,因此您可以传递匹配的 lambdas 而不是它。【参考方案2】:

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

Oracle Docs: Lambda Expressions Oracle Docs: Method References

例如,如果你想要一个功能接口A -&gt; 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&lt;A,B&gt;,但是包里还有很多其他的java.util.function。最值得注意的是

Supplier: void -&gt; A Consumer: A -&gt; void BiConsumer: (A,B) -&gt; void Function: A -&gt; B BiFunction: (A,B) -&gt; 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);

【讨论】:

对于回调,最好编写自己的函数式接口,因为每个回调类型通常都有多种语法。 我不确定。如果您正在寻找 java.util.function 中的课程之一,那么您就可以开始了。然后,您可以使用 I/O 的泛型。 (?) 你没听懂,我说的是把使用回调函数的 C++ 代码翻译成 Java 8,在那里,对于每个唯一的函数指针,你必须在 Java 中创建一个函数式接口,因为实际生产代码中会有更多参数。 我的结论是,大多数时候,您需要编写自己的功能接口,因为java.util.function 中提供的默认功能接口是不够的。 我添加了一个关于现有功能接口以及如何创建新接口的注释【参考方案3】:

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

private void runCallback(Runnable callback)

    // Run callback
    callback.run();

用法:

runCallback(new Runnable()

    @Override
    public void run()
    
        // Running callback
    
);

【讨论】:

我喜欢这个,因为我不需要创建一个新的接口或类来做一个简单的回调。感谢您的提示! public interface SimpleCallback void callback(Object... objects); 这很简单,可能很有用,您还需要传递一些参数。 @cprcrack 无法在 run() 函数中传递值? 这样做的美妙之处在于,您可以在传递函数时使用简短的 lambda 表达式,即 runCallback(() -&gt; /* running callback */ )。这对于简单的东西非常有用。对于更复杂的东西,我建议查看CompletableFutures。【参考方案4】:

但我发现有一种最喜欢的方式是我一直在寻找的......它基本上来自这些答案,但我不得不对其进行操作以更加冗余和高效..我认为每个人都在寻找什么我想出了

言归正传::

首先制作一个界面就这么简单

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是需要一些时间的函数,你想给它添加一个回调来通知你结果来了,添加回调接口作为这个方法的参数

希望我的意思很清楚,享受吧;)

【讨论】:

这很容易【参考方案5】:

有点吹毛求疵:

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

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

【讨论】:

是的,这就是我的意思。通过 30 个左右的活动,您最终会获得 30 个课程。【参考方案6】:

这在带有 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();
    

【讨论】:

万一吵架会这样吗? pastebin.com/KFFtXPNA 是的。只要保持正确的 lambda 语法,任何数量的参数(或没有)都可以工作。【参考方案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);
        
    

【讨论】:

看起来有点矫枉过正(/me ducks) 取决于使用的次数和项目的大小:对于少数类的项目来说过度杀伤力,在大的项目上实用。 还有,看看@monnoo的回答【参考方案8】:

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

已经提出了关于 Java 中的闭包的建议(它将提供您正在寻找的行为),但没有一个将包含在即将发布的 Java 7 版本中。

【讨论】:

“方法不是(还)Java 中的一流对象”——嗯,有方法类[1],您当然可以传递其中的实例。它不是您期望从 java 中获得的干净、惯用的 OO 代码,但它可能是权宜之计。不过,当然要考虑一些事情。 [1] java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/Method.html【参考方案9】:

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

【讨论】:

【参考方案10】:

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

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

【讨论】:

【参考方案11】:

您也可以使用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);
    

【讨论】:

【参考方案12】:

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

package ***Q443708_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 ();
    

【讨论】:

如何将 null 作为 arg 传递? @TWiStErRob,在此示例中,它将类似于 timer.SetTimer ((int)(Math.random ()*10), System.out, "printf", "%s: [%s]", new Object[]"null test", null);。输出将是null test: [null] args[i].getClass() 上不是 NPE 吗?我的观点是,如果您根据参数类型选择方法,它就不起作用。它适用于String.format,但可能不适用于接受null的其他东西。 @TWiStErRob,好点子!我添加了一个可以手动传递argTypes 数组的函数,所以现在我们可以传递null 参数/参数而不会发生NullPointerException。示例输出:NullParameterTest: String parameter=null, int parameter=888【参考方案13】:

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

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

【讨论】:

【参考方案14】:

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

我结合了 Daniel Spiewak (in his answer to this) 的建议;成功的步骤包括:捕获NoSuchMethodException -> getMethods() -> 通过method.getName() 寻找匹配的一个 -> 然后显式循环遍历参数列表并应用 Daniels 解决方案,例如识别类型匹配和签名匹配.

【讨论】:

【参考方案15】:

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

【讨论】:

【参考方案16】:
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 这是一个接口而不是一个类),然后跟随着接口方法本身的定义。 (*这个定义实际上是匿名类)。 然后你得到对象引用,通过它你可以调用方法本身。

【讨论】:

【参考方案17】:

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

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


现在在你想要回调的类中实现上面的创建接口。 并且还传递要回调的类的“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);
    

【讨论】:

【参考方案18】:

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

【讨论】:

以上是关于java回调函数问题?的主要内容,如果未能解决你的问题,请参考以下文章

回调函数Java接口回调 总结

Java/Android中的函数调用&回调函数&自定义回调函数

谁帮我解释下java中回调函数

在java中回调函数怎么理解,android中的回调函数和java中有区别吗?

在JAVA里 啥是回调函数啊.?

一个简单的java回调函数的实现