回調 是一種雙向調用模式:調用方 A 調用被調用方 B 時,將一段可執行代碼(函數/方法/對象)作為參數傳遞給 B,B 在適當時機主動調用這段代碼,將控制權「回調」給 A。
核心三要素:
// 被調用方直接依賴具體類
class Calculator {
public void add(int a, int b, Student student) {
int result = a + b;
student.fillBlank(a, b, result); // 直接調用 Student 方法
}
}
class Student { void fillBlank(int a, int b, int r) { ... } }
缺點:Calculator 綁定 Student,無法複用給 Seller、Teacher 等其他角色。
// 1. 定義回調契約
interface Callback { void onResult(int a, int b, int result); }
// 2. 被調用方依賴介面
class SuperCalculator {
public void add(int a, int b, Callback cb) {
int result = a + b;
cb.onResult(a, b, result); // 面向介面編程
}
}
// 3. 調用方實現介面(匿名內部類 / Lambda)
class Student implements Callback {
public void onResult(int a, int b, int result) { ... }
}
class Seller implements Callback {
public void onResult(int a, int b, int result) { ... }
}
// 使用
new SuperCalculator().add(1, 2, new Student());
new SuperCalculator().add(10, 20, new Seller());
// 函數式介面
@FunctionalInterface
interface Callback { void onResult(int a, int b, int result); }
// 使用 Lambda
calculator.add(100, 200, (a, b, r) -> System.out.println(a + "+" + b + "=" + r));
// 或方法引用
calculator.add(100, 200, this::handleResult);
| 維度 | 同步回調 | 異步回調 |
|---|---|---|
| 執行流程 | 調用方阻塞等待,被調用方執行完回調才返回 | 調用方立即返回,被調用方另一線程完成後回調 |
| 典型場景 | 集合遍歷、排序比較器、模板方法模式 | 網絡請求、文件 IO、資料庫查詢、事件監聽 |
| 線程模型 | 單線程 | 多線程/線程池/事件循環 |
| 錯誤處理 | try-catch 直接捕獲 | 回調方法內處理或傳遞異常對象 |
List list = Arrays.asList("b", "a", "c");
Collections.sort(list, (s1, s2) -> s1.compareTo(s2)); // 回調比較邏輯
// sort 內部會多次回調 compare 方法
// 定義異步回調介面
@FunctionalInterface
interface HttpCallback {
void onSuccess(String response);
void onFailure(Exception e);
}
// 非阻塞客戶端
class AsyncHttpClient {
private ExecutorService pool = Executors.newFixedThreadPool(10);
public void get(String url, HttpCallback cb) {
pool.submit(() -> {
try (CloseableHttpClient client = HttpClients.createDefault()) {
String resp = client.execute(new HttpGet(url),
response -> EntityUtils.toString(response.getEntity()));
cb.onSuccess(resp);
} catch (Exception e) {
cb.onFailure(e);
}
});
}
}
// 使用
client.get("https://api.example.com/data", new HttpCallback() {
public void onSuccess(String resp) { System.out.println("收到:" + resp); }
public void onFailure(Exception e) { e.printStackTrace(); }
});
System.out.println("主線程繼續執行,不阻塞");
多層異步回調嵌套導致代碼水平擴展、錯誤處理分散:
// 回調地獄
step1(() -> step2(() -> step3(() -> step4(() -> ...))));
解決方案演進:
CompletableFuture.supplyAsync(() -> step1())
.thenApply(r -> step2(r))
.thenApply(r -> step3(r))
.exceptionally(e -> { log.error(e); return fallback; });
Mono.just(input)
.flatMap(this::step1)
.flatMap(this::step2)
.subscribe(result -> ..., error -> ...);
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future f1 = scope.fork(() -> step1());
Future f2 = scope.fork(() -> step2(f1.get()));
scope.join().throwIfFailed();
String result = f2.get();
}
| 模式 | 回調角色 | 典型例子 |
|---|---|---|
| 模板方法 | 父類定義骨架,子類實現鉤子方法(回調) | AbstractList.iterator() |
| 策略模式 | 策略介面即回調契約 | Comparator、Runnable |
| 觀察者模式 | Listener/Observer 即回調 | EventListener、PropertyChangeListener |
| 訪問者模式 | accept(visitor) 回調 visit(this) | FileVisitor、AST 遍歷 |
| 命令模式 | Command.execute() 即回調 | ThreadPoolExecutor.execute(Runnable) |
@FunctionalInterface 標註單方法介面,啟用 LambdaonSuccess/onFailure/onProgress/onCompleteCompletableFuture.orTimeout()、CancellationToken回調是控制反轉 (IoC) 在方法級的體現:將「什麼時候做什麼」的決定權從被調用方交還給調用方。從介面回調到 Lambda、CompletableFuture、Reactive Streams、Virtual Threads,Java 不斷演進以更優雅的方式解決異步編程複雜度。理解回調本質,選擇適合業務場景的抽象層級,是寫出高併發、可維護系統的關鍵。