一、概念 對回調函數的概念一直不清晰,看過兩次,時間一長就忘光了,因此在這篇文章中針對Java中的回調函數及其相關內容進行梳理總結。 記得以前剛接觸回調函數是在非同步IO中,操作系統將數據從內核空間複製到用戶空間時會通過回調函數通知進程對數據進行處理,因此當時下意識的以為是線程間的一種通信機制;再後來 ...
一、概念
對回調函數的概念一直不清晰,看過兩次,時間一長就忘光了,因此在這篇文章中針對Java中的回調函數及其相關內容進行梳理總結。
記得以前剛接觸回調函數是在非同步IO中,操作系統將數據從內核空間複製到用戶空間時會通過回調函數通知進程對數據進行處理,因此當時下意識的以為是線程間的一種通信機制;再後來顧名思義以為就是一個函數A調用函數B,執行B中的內容時會對A產生反饋,指導函數A的走向。
定義:回調函數被當成參數傳遞給中間函數,因此在傳入一個回調函數之前,中間函數是不完整的,主函數在調用中間函數時,需要先執行回調函數,通過傳入不同的回調函數,可以決定和改變中間函數的行為。
可見回調函數的調用是三個函數之間的關係,而不是想當然的兩個函數。這三個函數分別是:主函數(調用函數)、中間函數和回調函數。下麵的圖片1-1,可以說明三個函數之間的關係。
圖1-1三個函數的調用關係圖
為了防止以後不清楚再多說幾句,主函數A在調用中間函數B時,需要將回調函數C作為B參數傳入,因此在B函數體中走到要執行的特定位置時就會調用C來進行處理,因此C不但能監視B執行的位置(運行狀態),而且能幹預B的運行。而C作為參數傳入由B調用與由B直接調用的區別就是:前者只有A在調用B的時候才能確定C的內容,後者在程式編譯或者寫完時就已經確定C的內容了,前者因此更具靈活性。因此,回調函數其實就是多態的應用,能實現運行中的動態綁定。
二、實現
回調函數有同步和非同步兩種方式,現在我們將分別用Java語句來對其的調用過程進行實現。
2.1同步模式
首先回調函數我們最好設計為一個介面,這樣便於擴展
public interface CallBack { //回調函數checkWork,學生完成作業後 void checkWork(); } |
然後設計中間函數
class Student{ int a =1,b=2,c; Boolean flag; //中間函數doWork public void doWork(CallBack callback) { System.out.println("學生開始做作業。。。"); c=4;//學生開始做計算題,並得出結果 System.out.println("學生作業完成,通知老師檢查"); callback.checkWork(); if(flag) { System.out.println("結果正確,學生回家。。"); }else { System.out.println("結果不正確,繼續修改。。"); }}} |
最後由主函數進行調用
public class Test { public static void main(String[] args){ System.out.println("老師佈置作業。。"); Student student = new Student(); student.doWork(new CallBack(){ @Override public void checkWork() { if(student.c==3) { student.flag=true; }else { student.flag=false; }}}); System.out.println("老師回家了。。。"); } } |
調用過程如下:主函數調用中間函數即學生開始做作業計算a+b的值,完成之後通知老師即中間函數調用回調函數進行處理,將影響中間函數的走向,根據處理結果決定學生是否可以回家。
執行結果如下:
老師佈置作業。。 學生開始做作業。。。 學生作業完成,通知老師檢查 結果不正確,繼續修改。。 老師回家了。。。 |
2.2非同步模式
中間函數與回調函數保持不變,在主函數開一個線程進行函數的調用。
public class Test { public static void main(String[] args){ System.out.println("老師佈置作業。。"); //開啟一個新的線程,將調用過程放入其中執行 //主線程可以繼續向下執行 Runnable runnable = new Runnable() { @Override public void run() { Student student = new Student(); student.doWork(new CallBack(){ @Override public void checkWork() { if(student.c==3) { student.flag=true; }else { student.flag=false; }}}); }}; Thread t = new Thread(runnable); t.start(); System.out.println("老師回家了。。。"); }} |
執行結果如下:
老師佈置作業。。 老師回家了。。。 學生開始做作業。。。 學生作業完成,通知老師檢查 結果不正確,繼續修改。。 |
總之,回調模式在生活中的例子非常常見,在編程中最常見的就是各種GUI編程裡面的按鈕點擊什麼的,通過回調可以將控制權轉移,配合上非同步模式,可以讓系統設計的更加優雅。
參考文獻:https://www.zhihu.com/question/19801131/answer/27459821
https://mp.weixin.qq.com/s/xn0Pa_Ilp0FjL_N_aswZtQ