熟悉 MS-Windows 或 X Window System 事件驅動程式設計的開發者,習慣傳遞函數指標作為回調函數,在特定事件發生時自動被呼叫。然而,Java 的物件導向模型並不支援方法指標,乍看之下似乎無法使用這套優雅的機制。但實際上,Java 透過介面提供了達成相同功能的替代方案。
關鍵在於定義一個簡單介面,並在其中宣告我們期望被呼叫的方法。例如,假設我們希望在某個事件發生時收到通知,可以定義如下介面:
public interface InterestingEvent {
// 這是一個常規方法,可依需求擁有回傳值或接收參數
public void interestingEvent();
}
這樣一來,我們就能控制實作該介面之類別的任何物件,而無需關心外部型別資訊。相較於在 C++ 搭配 Motif 時,利用視窗元件的資料欄位存放物件指標、再搭配難以控制的 C 函數,這種做法乾淨且型別安全得多。
發出事件訊號的類別必須持有實作 InterestingEvent 介面的物件參考,並在適當時機呼叫 interestingEvent() 方法。
public class EventNotifier {
private InterestingEvent ie;
private boolean somethingHappened;
public EventNotifier(InterestingEvent event) {
// 保存事件物件以備後用
ie = event;
// 尚無待報告事件
somethingHappened = false;
}
public void doWork() {
// 檢查在別處設定的謂詞
if (somethingHappened) {
// 透過呼叫介面方法發出事件訊號
ie.interestingEvent();
}
// ...
}
// ...
}
範例中使用 somethingHappened 旗標追蹤是否應觸發事件。實務上,呼叫此方法本身往往足以保證向 interestingEvent() 發出訊號。
想要接收事件通知的程式碼,必須實作 InterestingEvent 介面,並將自身參考傳遞給事件通知器。
public class CallMe implements InterestingEvent {
private EventNotifier en;
public CallMe() {
// 建立事件通知器,並傳遞自身參考
en = new EventNotifier(this);
}
// 定義事件的實際處理器
public void interestingEvent() {
// 噢!必定發生了感興趣的事件!
// 執行某些操作...
}
// ...
}
Java 雖無方法指標,但透過介面導向設計,即可優雅地實現回調機制。這不僅保留了型別安全,更符合物件導向封裝與解耦的原則,是 Java 事件驅動程式設計的基石之一。