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


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

一、回調的意義

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

不如先思考兩個問題:

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

而在本例中,回調方式被用來處理爬取后的大量返回數據。在業務層面,這些數據被安排在調用方進行處理,但是調用方卻沒有處理這些數據的足夠權限。於是,通過回調,業務被很好的分層並且執行。

二、如何實現同步回調

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

  1. 回調方調用調用方進行數據爬取
  2. 調用方調用回調方進行數據存儲
  3. 調用方調用回調方進行日志記錄

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

相應代碼如下:

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 實現同步回調有許多方式,為什么我們要通過匿名內部類的方式來實現回調,直接回調不香嗎?

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

相應代碼如下:

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());
    }
}

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

五、總結

  1. 常規類不保證接口安全性:常規接口通常可以設定權限,但不可以指定訪問類,也就是說要么都可以訪問,要么都拒絕訪問。而內部類中接口可以指定訪問類。

  2. 內部類保證接口安全性:內部類接口通常是對外隱藏的,那么如何使得內部類對指定訪問類暴露呢?方法很簡單,只需要通過外部類實例化內部類,並對指定類傳參,便可以使得指定類對內部類可訪問。

  3. 內部類的安全性加上其對外部類的完全權限,這使得其成為實現回調的首選方案。在JAVA8中,lambda表達式本質上就是匿名內部類的語法糖。

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

六、擴展

最后留幾個很有意思的思考問題:

  1. 返回值的三種方法?
  2. 同步調用與同步回調?
  3. 異步調用與異步回調?
  4. 異步的職責划分?
  5. 回調的最佳實現?(本文給出了回答)

參考鏈接

[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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM