先来聊接口回调
接口回调
这是一种设计模式,我的理解:
共有三者:调用者,被调用者和接口方法
接口回调的核心在于:调用者只需要实现接口方法,而被调用者只需要在合适的时机反过来调用调用者实现的方法,通过第三者接口方法,实现了双向解耦,调用者不关心被调用者,被调用者不关心调用者如何实现方法的。
回调的核心思想是:
- 调用者(caller) 提供一个实现,交给 被调用者(callee)。
- 被调用者在适当的时机,通过调用该实现来执行相应的逻辑。
"回调"的体现在于:正常都是调用者调用被调用者,而这里是被调用者主动调用调用者
接口回调的实现步骤
1. 定义接口
- 定义一个接口,其中包含需要被回调的方法。
2. 实现接口
- 由调用者(caller)实现这个接口,并提供具体的回调逻辑。
3. 注册接口
- 将实现接口的对象传递给被调用者(callee)。
4. 在合适时机回调
- 被调用者在需要时调用接口中的方法,触发回调逻辑。
举例理解
举一个生活中的例子,比如点外卖
这涉及到三者关系:
- app用户(调用者)
- 饿了么app(被调用者)
- 联系方式(接口方法实现)
- 【用户】填写了联系方式,比如地址和手机号(实现接口方法)
- 付款后,【饿了么】开始一系列操作(呼叫商家 ->呼叫骑手等)
- 无论如何,最终送餐的时候必须知道如何联系【用户】,会调用实现的接口方法
- 最终送到【用户】手中
这个例子中,想想看,【用户】作为调用者需要关心【饿了么】的一系列流程操作吗?并不需要。【饿了么】需要关心【用户】的联系方式吗?呃。。。。好像的确挺关心的,这个例子翻车了?反正你能理解就行,联系方式就算写错成其他户的地址,【饿了么】的骑手也会照样送过去,这么看也不算关心内容了。
代码案例
写一个简单的代码案例,加深理解
public interface buttonclicklistener { void click(); // 回调方法 } public class button { private buttonclicklistener listener; public void setbuttonclicklistener(buttonclicklistener listener){ this.listener = listener; } public void click(){ if (listener != null){ listener.click(); // 回调接口方法 } } } public class main implements buttonclicklistener{ public static void main(string[] args) { // 创建被调用者实例 button button = new button(); // 创建调用者(main)实例 main main = new main(); // 注册回调接口 button.setbuttonclicklistener(main); // 模拟按钮点击 button.click(); } @override public void click() { system.out.println("点击按钮"); } }
常见的接口回调
举一个常用的consumer接口经常出现接口回调现象
关于consumer接口可以看我另一篇博客:【java】java8的4个函数式接口简单教程
arrays.aslist("a", "b", "c") .foreach(s -> system.out.println(s));
如上,这行代码其实发生了隐式的接口回调
三者关系:
- 我(调用者):我调用了集合的foreach()方法,并且我实现了consumer接口的抽象方法accept()
- foreach()(被调用者):foreach()方法会自动调用我实现的accept()方法
- consumer.accept()(接口方法)
我只需要实现接口方法,foreach()会主动调用我实现的方法,并不关心我到底咋实现的
异步回调
上面说的都是同步回调
- 回调方法在调用的过程中立即被执行。
- 示例:按钮点击监听。
异步回调是:
- 回调方法在调用结束后,异步地被执行(通常是在另一个线程中)。
- 示例:网络请求完成后通知调用者。
// 异步回调示例(模拟异步任务) public class asynctask { // 定义回调接口 public interface taskcallback { void ontaskcomplete(string result); } // 执行异步任务 public void execute(taskcallback callback) { new thread(() -> { try { thread.sleep(2000); // 模拟耗时任务 string result = "task finished!"; callback.ontaskcomplete(result); // 回调通知任务完成 } catch (interruptedexception e) { e.printstacktrace(); } }).start(); } } // 调用者代码 public class main { public static void main(string[] args) { asynctask task = new asynctask(); task.execute(result -> system.out.println("callback received: " + result)); system.out.println("task started..."); } }
注意事项
空指针检查
- 如果没有设置回调接口(即
listener
为null
),直接调用接口方法会抛出nullpointerexception
。
if (listener != null) { listener.onclick(); }
线程安全
- 在多线程场景下,注意回调接口的线程安全问题,例如异步任务回调需要在主线程中执行。
性能开销
- 回调接口的调用会带来一定的性能开销,尤其是在高频调用场景中,可能对性能产生影响。
强依赖问题
- 如果接口实现的方式过于复杂,会导致代码难以维护,因此需要设计合理的接口和实现。
方法回调
差不多,看区别就行
核心记住一句话:将方法作为另一个方法的参数
方法回调:
- 调用者将某个对象的方法引用直接传递给被调用者,被调用者在适当时机调用这个方法。
- 不依赖接口,通常通过方法引用或 lambda 表达式实现。
依赖接口 | 不需要接口,直接传递方法引用 | 需要接口,调用者实现接口并提供逻辑 |
灵活性 | 不灵活,直接绑定到具体类的方法 | 更灵活,支持不同实现类 |
解耦程度 | 较低,调用者和被调用者直接关联 | 高度解耦,通过接口隔离调用者与实现者 |
复杂性 | 简单 | 相对复杂,但更适合扩展场景 |
- 方法回调:简单直接调用具体类的方法,适用于调用单一方法的场景。
- 接口回调:强调解耦,适用于需要灵活多变的回调逻辑场景。
总结就行,区别不大,就是不用实现接口了
三者关系就变成,我调用foreach(),而foreach()调用了我写的caller.printmessage()方法
总结
到此这篇关于java接口回调和方法回调的文章就介绍到这了,更多相关java接口回调和方法回调内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论