一、什么是回调(Callback)?
回调是一种常见的编程模式,允许一个函数在特定事件或任务完成后,调用另一个预先定义的函数。在Java中,由于不支持函数指针,通常通过接口或Lambda表达式实现回调。
核心思想:
定义回调接口:声明回调方法。
传递接口实现:将回调逻辑传递给调用方。
触发回调:在特定时机(如任务完成、事件发生)调用回调方法。
二、回调的典型使用场景
事件处理:如按钮点击、数据加载完成。
异步任务:如网络请求完成后通知主线程。
自定义逻辑注入:如排序算法中自定义比较规则。
框架扩展点:如Spring的ApplicationListener。
三,代码实现
基础:
// 1. 定义回调接口
interface OrderCallback {
void onOrderCompleted(String orderId, boolean isSuccess);
}
// 2. 业务类
class OrderService {
public void processOrder(String orderId, OrderCallback callback) {
System.out.println("处理订单: " + orderId);
// 模拟订单处理逻辑
boolean isSuccess = Math.random() > 0.5;
// 触发回调
callback.onOrderCompleted(orderId, isSuccess);
}
}
// 3. 客户端调用
public class CallbackDemo {
public static void main(String[] args) {
OrderService orderService = new OrderService();
orderService.processOrder("ORDER_123", new OrderCallback() {
@Override
public void onOrderCompleted(String orderId, boolean isSuccess) {
System.out.println("订单 " + orderId + " 处理结果: " + (isSuccess ? "成功" : "失败"));
}
});
}
}
讲解:
这段代码就像你点外卖的整个过程,我来用最简单的话解释清楚:首先,你(客户端)在手机上下单后,商家(OrderService)开始做饭,但做饭需要时间,你不能一直盯着手机等。于是你告诉商家:“做好后打电话告诉我结果”(这就是回调接口 )。商家做好饭后,不管是成功做好了还是失败没材料了,都会按照你留的电话打给你说“订单号XXX,成功/失败了”(这就是调用 onOrderCompleted 方法通知结果)。而你在下单时写的那个匿名类,就相当于把电话号码和接到电话后要做的动作(比如打印结果)一次性告诉商家,这样商家处理完订单就能自动通知你,你不需要干等着,这就是回调机制——事情办完了自动通知你结果,整个过程像自动化的电话回复。
进阶:
// 1. 定义回调接口
interface DownloadCallback {
void onDownloadSuccess(String filePath);
void onDownloadFailure(String error);
}
// 2. 异步任务类
class DownloadTask {
public void downloadFile(String url, DownloadCallback callback) {
new Thread(() -> {
try {
System.out.println("开始下载: " + url);
Thread.sleep(2000); // 模拟下载耗时
// 模拟下载成功
String filePath = "/downloads/" + url.substring(url.lastIndexOf('/') + 1;
callback.onDownloadSuccess(filePath);
} catch (InterruptedException e) {
callback.onDownloadFailure("下载中断");
} catch (Exception e) {
callback.onDownloadFailure("网络错误");
}
}).start();
}
}
// 3. 客户端调用(模拟UI更新)
public class AsyncCallbackDemo {
public static void main(String[] args) {
DownloadTask downloadTask = new DownloadTask();
downloadTask.downloadFile("https://example.com/file.zip", new DownloadCallback() {
@Override
public void onDownloadSuccess(String filePath) {
System.out.println("下载完成,文件路径: " + filePath);
// 实际场景中可更新UI
}
@Override
public void onDownloadFailure(String error) {
System.err.println("下载失败: " + error);
}
});
System.out.println("主线程继续执行其他任务...");
}
}
讲解:
想象你让室友帮你下电影。你不会傻站在他电脑前干等,而是说"下好了把存哪儿微信发我,出问题就吱一声"——这就是回调接口的精髓,提前约定好通知方式。
代码里的DownloadTask就像你室友,它接到任务后直接开个新线程干活(就像你室友搬出自己笔记本电脑开始下载),这时候你完全不用干瞪眼等着,该刷剧刷剧该打游戏打游戏(主线程继续输出"我先去忙别的")。
如果2分钟后顺利下完,你手机就会叮咚一声收到他发的文件路径(触发onDownloadSuccess);要是中途突然断网,你立马就能收到他的吐槽"淦!没网了下个锤子"(触发onDownloadFailure)。
整个过程就像现实托人办事:你把任务交代清楚(定义回调方法),对方领命而去(开新线程),你该干嘛干嘛(主线程不阻塞)。等对方搞定/搞砸了,自然会按约定方式给你反馈。这种异步回调就像当甩手掌柜,把耗时的脏活累活扔到后台,结果出来自动提醒你善后,效率直接拉满!
优点
解耦性强
场景:A 模块调用 B 模块完成任务,B 完成后通知 A,但 A 不需要知道 B 的具体实现。
支持异步处理
场景:耗时操作(如网络请求、文件下载)不阻塞主线程。
灵活扩展
场景:同一接口可定义多种回调行为。
缺点
回调地狱
多层嵌套回调导致代码难以阅读和维护。
例如:
login(user, new LoginCallback() {
@Override
public void onSuccess() {
getProfile(new ProfileCallback() {
@Override
public void onSuccess(Profile profile) {
downloadAvatar(profile.getUrl(), new DownloadCallback() {
@Override
public void onSuccess(String path) {
System.out.println("最终成功!");
}
});
}
});
}
});
错误处理复杂
场景:每个回调需单独处理异常,代码冗余。
可读性差
代码分散
优化:
回调是小规模异步任务的利器,但面对复杂逻辑时,优先考虑 CompletableFuture 或 响应式编程框架
总结:
| 场景 | 用回调 | 用替代方案 |
|---|---|---|
| 简单异步通知 | 直接易用 | 过度设计 |
| 多步骤异步任务 | 回调地狱 | CompletableFuture |
| 需要统一错误处理 | 冗余代码 | 异常传播链 |
| UI 事件监听 | 匿名类或 Lambda | 无需替代 |


被折叠的 条评论
为什么被折叠?



