用匿名內部類實現 Java 同步回撥

小z同學發表於2020-10-28

在一個應用系統中,不論使用何種程式語言,模組之間要進行呼叫,僅存在三種方式:同步呼叫、非同步呼叫、回撥。本文就其中回撥方式進行詳細解讀,並通過匿名內部類的手段,在最後實現一個同步回撥的過程。

一、回撥的意義

在學習回撥之前,我們需要知道使用回撥的原因,和回撥的應用場景。

不如先思考兩個問題:

  1. 棧底對棧頂通常是不可見的,但是棧頂有時需要直接呼叫棧底
  2. 上級派下級做事,在此期間,下級可能需要通過上級獲取高許可權的協助

而在本例中,回撥方式被用來處理爬取後的大量返回資料。在業務層面,這些資料被安排在呼叫方進行處理,但是呼叫方卻沒有處理這些資料的足夠許可權。於是,通過回撥,業務被很好的分層並且執行。

二、如何實現同步回撥

本文對同步回撥的業務需求如下:

  1. 回撥方呼叫呼叫方進行資料爬取
  2. 呼叫方呼叫回撥方進行資料儲存
  3. 呼叫方呼叫回撥方進行日誌記錄

根據需求可以得到回撥過程的時序圖:

用匿名內部類實現 Java 同步回撥

相應程式碼如下:

public interface Handler {
    void handle(String info);
}

public class Task {
    private String info;

    private void setInfo(String info) {
        this.info = info;
    }

    public void call() {
        Crawler.getInstance().crawl(new Handler() {
            @Override
            public void handle(String info) {
                setInfo(info);
            }
        });
    }
}

public class Crawler {
    private static Crawler instance = null;

    public static Crawler getInstance() {
        if (instance == null) {
            instance = new Crawler();
        }
        return instance;
    }

    private String getInfo() {
        return "the info from crawler";
    }

    public void crawl(Handler handler) {
        handler.handle(getInfo());
    }
}

三、遇到的問題

如果我們使用程式碼來實現上述回撥過程,不難會發現這樣一個問題:Task呼叫Crawler,Crawler呼叫Handler,Hanlder呼叫Task。很明顯,此處存在一個環,產生了迴圈依賴的問題,而介面可以為我們提供良好的解決方案。

用匿名內部類實現 Java 同步回撥

四、為什麼通過匿名內部類的方式

用 Java 實現同步回撥有許多方式,為什麼我們要通過匿名內部類的方式來實現回撥,直接回撥不香嗎?

不妨先看看直接回撥的順序圖:

用匿名內部類實現 Java 同步回撥

相應程式碼如下:

public interface Handler {
    void handle(String info);
}

public class Task implements Handler{
    private String info;

    private void setInfo(String info) {
        this.info = info;
    }

    public void call() {
        Crawler.getInstance().crawl(this);
    }

    @Override
    public void handle(String info) {
        setInfo(info);
    }
}

public class Crawler {
    private static Crawler instance = null;

    public static Crawler getInstance() {
        if (instance == null) {
            instance = new Crawler();
        }
        return instance;
    }

    private String getInfo() {
        return "the info from crawler";
    }

    public void crawl(Handler handler) {
        handler.handle(getInfo());
    }
}

直接回撥帶來的最大問題便是回撥介面的暴露,也就是說回撥介面不一定用於回撥,也可以用於直接訪問。這在業務層面的設計上是絕對不允許的,而匿名內部類在執行回撥等特定業務的同時,可以很好的對外隱藏用於回撥的介面。

五、總結

常規類通常無法對回撥等特定介面作出限定,要麼都可以訪問,要麼都拒絕訪問。而內部類通過犧牲自身的被訪問許可權,提升了自身訪問外部類的能力,這使得其成為實現回撥的首選方案。在JAVA8中,lambda表示式本質上就是匿名內部類的語法糖。

注:匿名內部類本質上是成員內部類、區域性內部類的簡化寫法,這裡將其統稱為內部類。

參考連結

[1] <<Java核心技術>> 卷一
[2] https://www.cnblogs.com/xrq730/p/6424471.html
[3] https://www.cnblogs.com/heshuchao/p/5376298.html
[4] https://www.cnblogs.com/dolphin0520/p/3811445.html

相關文章