Java回調的四種寫法(反射、直接調用、接口調用、Lambda表達式)

1. 引言

在計算機程序設計中,回調函數,簡稱回調(Callback),是指經過函數參數傳遞到其餘代碼的,某一塊可執行代碼的引用。這一設計容許了底層代碼調用在高層定義的子程序。java

以上是維基百科對「回調函數」的定義。對於回調,不一樣的語言有不一樣的回調形式,例如:異步

  • C、C++ 容許將函數指針做爲參數傳遞;
  • JavaScript、Python 容許將函數名做爲參數傳遞。

本文將介紹 Java 實現回調的四種寫法:ide

  • 反射;
  • 直接調用;
  • 接口調用;
  • Lambda表達式。

在開始以前,先介紹下本文代碼示例的背景,在 main 函數中,咱們異步發送一個請求,而且指定處理響應的回調函數,接着 main 函數去作其餘事,而當響應到達後,執行回調函數。函數

2. 反射

Java 的反射機制容許咱們獲取類的信息,其中包括類的方法。咱們將以 Method 類型去獲取回調函數,而後傳遞給請求函數。示例以下:spa

Request 類中的 send 方法有兩個參數 clazz、method,分別是Class 類型和 Method 類型,這裏的 method 參數就是待傳入的回調函數,而爲了經過 invoke 方法進行反射調用,還須要一個實例,因此將回調函數所在的類的 Class 對象做爲參數傳遞進來,經過 newInstance 構造一個對象,將順利經過 invoke 反射調用。線程

public class Request{
    public void send(Class clazz, Method method) throws Exception {
        // 模擬等待響應
        Thread.sleep(3000);
        System.out.println("[Request]:收到響應");
        method.invoke(clazz.newInstance());
    }
}
複製代碼

CallBack 類很簡單,只有一個 processResponse 方法,用於看成回調函數,處理響應。設計

public class CallBack {
    public void processResponse() {
        System.out.println("[CallBack]:處理響應");
    }
}
複製代碼

咱們在 main 方法中,新開了一個線程去發送請求,而且把須要的 CallBack.class 和 processResponse 方法傳遞進去。指針

public class Main {
    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我開個線程去異步發請求");
        new Thread(() -> {
            try {
                request.send(CallBack.class, CallBack.class.getMethod("processResponse"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:請求發完了,我去幹點別的");
        Thread.sleep(100000);
    }
}
/** Output: [Main]:我開個線程去異步發請求 [Main]:請求發完了,我去幹點別的 [Request]:收到響應 [CallBack]:處理響應 */
複製代碼

這種寫法須要傳遞的參數十分繁瑣。下面介紹一種簡單的寫法,直接調用。code

3. 直接調用

咱們來改寫下 send 方法的參數,改成一個 CallBack 類型參數。以下:cdn

在 send 方法中咱們不使用反射,改成直接經過對象來調用方法。

public class Request{
    public void send(CallBack callBack) throws Exception {
        // 模擬等待響應
        Thread.sleep(3000);
        System.out.println("[Request]:收到響應");
        callBack.processResponse();
    }
}
複製代碼

main 函數中,咱們 new 了一個 CallBack 對象做爲參數傳遞給 send 方法。

public class Main {
    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我開個線程去異步發請求");
        CallBack callBack = new CallBack();
        new Thread(() -> {
            try {
                request.send(callBack);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:請求發完了,我去幹點別的");
        Thread.sleep(100000);
    }
}
複製代碼

這種實現方式十分簡單,可是存在的問題是不符合修改封閉原則。也就是說當咱們想要換一種「處理響應」的方法時,將必須去修改 CallBack 類的 processRequest()方法。而若是將 CallBack 類改成接口,咱們就能夠僅更換 CallBack 的實現了。下面請看接口調用的寫法。

4. 接口調用

首先將 CallBack 類改成接口。

public interface CallBack {
    public void processResponse();
}
複製代碼

再新增一個 CallBack 接口的實現類 CallBackImpl。

public class CallBackImpl implements CallBack {
    @Override
    public void processResponse() {
        System.out.println("[CallBack]:處理響應");
    }
}
複製代碼

Request 類不變。Main 類中的 main 方法將實例化一個 CallBackImpl,而後經過 CallBack 接口傳遞進去。

public class Main {
    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我開個線程去異步發請求");
        CallBack callBack = new CallBackImpl();
        new Thread(() -> {
            try {
                request.send(callBack);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:請求發完了,我去幹點別的");
        Thread.sleep(100000);
    }
}
複製代碼

5. Lambda表達式

上述方法已經介紹的差很少了,最後咱們再介紹一種更加簡潔的寫法,經過使用 Lamda 表達式,將不用新增一個 CallBack 接口的實現類。下面請看改寫的 main 方法:

public class Main {
    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我開個線程去異步發請求");
        new Thread(() -> {
            try {
                request.send(()-> System.out.println("[CallBack]:處理響應"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:請求發完了,我去幹點別的");
        Thread.sleep(100000);
    }
}
複製代碼

咱們既不用去新增實現類,也不用去實例化,只須要傳遞 Lambda 表達式就能夠完成回調了。

6. 總結

爲了讓你們更好的理解回調,本文一共介紹了 4 種寫法,你們均可以根據本身的須要自取。

喜歡我文章的小夥伴,能夠掃碼關注下個人公衆號:「草捏子」

相關文章
相關標籤/搜索