回調是「好萊塢原則」(Don't call us, we'll call you)在代碼層面的體現。Java 無原生函數指針,但通過接口 + 實例傳遞完美實現回調。本文從同步回調、異步回調、閉包回調三個維度,結合經典「項目經理-程序員」場景與 JDK 源碼模式,徹底講透 Java 回調機制。
項目經理分派調研任務,程序員逐步匯報進度,經理即時驗收,直到通過為止。主線程阻塞等待任務完成。
// 1. 回調接口
public interface CallbackInterface {
boolean check(int result); // 返回 true 表示任務結束
}
// 2. 被調用者:程序員
public class Programmer {
public void study(CallbackInterface callback) {
int result = 0;
do {
result++;
System.out.println("第 " + result + " 次研討成果");
} while (!callback.check(result)); // 同步阻塞調用
System.out.println("調研任務結束");
}
}
// 3. 調用者:項目經理(實現回調接口)
public class Manager implements CallbackInterface {
private Programmer programmer;
public Manager(Programmer programmer) {
this.programmer = programmer;
}
public void entrust() {
arrange();
}
private void arrange() {
System.out.println("Manager 正在為 Programmer 安排任務");
programmer.study(this); // 傳遞 this 作為回調
System.out.println("任務安排完成,Manager 做其他事");
}
@Override
public boolean check(int result) {
return result == 5; // 第 5 次通過
}
}
// 4. 客戶端:總監
public class Boss {
public static void main(String[] args) {
new Manager(new Programmer()).entrust();
}
}
Manager 正在為 Programmer 安排任務
第 1 次研討成果
第 2 次研討成果
第 3 次研討成果
第 4 次研討成果
第 5 次研討成果
調研任務結束
任務安排完成,Manager 做其他事
Boss → Manager.entrust → Manager.arrange → Programmer.study → Manager.check (多次) → 返回 → Manager.arrange 繼續經理分派任務後立即返回處理其他事務,程序員在後台線程工作,完成後通知經理。
public class Programmer {
public void study(CallbackInterface callback) {
new Thread(() -> {
int result = 0;
do {
result++;
System.out.println("第 " + result + " 次研討成果");
} while (!callback.check(result));
System.out.println("調研任務結束");
}).start(); // 異步執行
}
}
Manager 正在為 Programmer 安排任務
任務安排完成,Manager 做其他事 ← 經理先繼續執行
第 1 次研討成果
第 2 次研討成果
...
調研任務結束
check() 在**工作線程**中執行,若需更新 UI 或訪問線程不安全資源,需自行切換線程(如 SwingUtilities.invokeLater、Handler.post)。CompletableFuture、ListenableFuture、RxJava 等高級異步抽象。閉包 = 函數 + 捕獲的環境變量。Java 8 前通過匿名內部類模擬,Java 8+ 使用 Lambda 表達式(實質是函數式接口實例)。
interface Incrementable { void increment(); }
class Callee1 implements Incrementable {
private int i = 0;
public void increment() { System.out.println(++i); }
}
public class Client {
public static void main(String[] args) {
new Callee1().increment(); // 直接調用,無回調
}
}
class Caller {
private Incrementable callback;
public Caller(Incrementable cb) { this.callback = cb; }
public void go() { callback.increment(); } // 回調
}
public class Client {
public static void main(String[] args) {
Callee1 c = new Callee1();
new Caller(c).go(); // Callee1 作為回調實現
}
}
假設第三方庫定義了 MyIncrement,我們想同時支持 Incrementable 與 MyIncrement:
// 第三方接口,無法修改
class MyIncrement {
public void increment() { System.out.println("MyIncrement.increment"); }
static void f(MyIncrement mi) { mi.increment(); }
}
// 我們的類繼承第三方類,並內部實現 Incrementable
class Callee2 extends MyIncrement {
private int i = 0;
public void increment() {
super.increment(); // 調用父類邏輯
System.out.println(++i);
}
// 閉包:私有內部類捕獲外部 this
private class Closure implements Incrementable {
public void increment() {
Callee2.this.increment(); // 訪問外部實例
}
}
// 對外暴露工廠方法
public Incrementable getCallbackReference() {
return new Closure();
}
}
public class Client {
public static void main(String[] args) {
Callee2 c = new Callee2();
// 作為 Incrementable 回調傳給 Caller
new Caller(c.getCallbackReference()).go();
// 也可直接作為 MyIncrement 使用
MyIncrement.f(c);
}
}
Callee2.this.increment() 訪問外部實例狀態。getCallbackReference() 返回接口類型,隱藏具體類。() -> this.increment(),但匿名內部類寫法更利於理解閉包本質。| 場景 | 回調接口 | 調用方 | 模式 |
|---|---|---|---|
| 集合排序 | Comparator.compare() | Collections.sort() | 同步 |
| 線程啟動 | Runnable.run() | Thread.start() | 異步 |
| IO 完成通知 | CompletionHandler | AsynchronousSocketChannel | 異步 |
| 事件監聽 | ActionListener.actionPerformed | JButton/Button | 異步(EDT) |
| Spring JdbcTemplate | RowCallbackHandler | query() | 同步 |
| Netty ChannelHandler | channelRead() | EventLoop | 異步 |
| CompletableFuture | Consumer/BiConsumer | thenAccept/whenComplete | 異步鏈式 |
CompletableFuture、Reactor、RxJava 鏈式 API。WeakReference 或註冊/註銷機制。@FunctionalInterface,支持 Lambda,提升可讀性。Java 回調機制的本質是接口引用傳遞,三種模式解決不同並發需求:
現代 Java 開發中,CompletableFuture + Lambda 已成為異步回調的標準範式,但理解底層接口機制對閱讀框架源碼、排查線程問題、設計擴展點仍不可或缺。