Java 是如何利用接口避免函數(shù)回調(diào)的方法
一、引言
在許多編程語言中,都有函數(shù)回調(diào)這一概念。C 和 C++ 中有函數(shù)指針,因此可以將函數(shù)作為參數(shù)傳給其它函數(shù),以便過后調(diào)用。而在 JavaScript 中,更是將函數(shù)回調(diào)發(fā)揮到了極致,各種事件的處理,特別是異步事件,基本都靠函數(shù)回調(diào)來完成。
在 Java 中,同樣可以實現(xiàn)函數(shù)回調(diào)。雖然沒有函數(shù)指針,但 Java 可以通過反射機制來獲得一個類的方法,將其以 java.lang.reflect.Method 類型參數(shù)傳遞給其它函數(shù),然后通過 Method 對象的 invoke 方法來調(diào)用該函數(shù)。
盡管如此,這種方式的調(diào)用步驟相對繁瑣、執(zhí)行效率低、難以調(diào)試。在 Java 中,有比函數(shù)回調(diào)更加優(yōu)雅的機制,那就是接口。
二、為什么需要函數(shù)回調(diào)
函數(shù)回調(diào),實際上是延遲實現(xiàn)某些功能的一種方式。
如果我們事先知道程序應(yīng)該執(zhí)行哪些操作,那么完全不需要函數(shù)回調(diào),直接在編程時實現(xiàn)即可。
但很多時候,在編寫代碼時,特別是寫工具類、功能庫或框架時,實現(xiàn)的是相對通用和抽象的功能,而具體場景下的功能則由使用這些類的開發(fā)者來實現(xiàn)。
函數(shù)回調(diào),可以解決這種事先不知道具體實現(xiàn)的情況。
排序函數(shù)的例子
舉例來說,當(dāng)我們要實現(xiàn)一個通用的排序函數(shù)時,事先并不知道其他開發(fā)者會用該函數(shù)來對哪些類型的元素進行排序,也就不知道以何種標(biāo)準來判斷這些元素的偏序(大?。╆P(guān)系。
因此,可以要求其他開發(fā)者在使用排序函數(shù)時,必須提供一個比較函數(shù) compare,這樣我們就可以用 compare 比較待排序元素的大小,而無需事先知道元素是什么類型,也無需知道 compare 的具體實現(xiàn)。
這里 compare 函數(shù)對于排序函數(shù)來說,就是回調(diào)函數(shù)。
偽代碼表示如下:
//通用的排序函數(shù) void sort(Object[] array, Method compare) { //利用 compare 函數(shù)比較 array 中元素的大小關(guān)系 //以便對 array 進行排序 } //由調(diào)用者實現(xiàn)具體的比較函數(shù) int compare(Object a, Object b) { //比較元素a、b,并返回大小關(guān)系 }
異步處理函數(shù)的例子
再比如說,當(dāng)我們編寫一個異步處理函數(shù)時,事先不知道其他開發(fā)者在處理完成時要進行哪些操作,因為這些操作只有在特定場景下使用該函數(shù)時才能知道。
于是可以要求開發(fā)者在使用該函數(shù)時,提供一個回調(diào)函數(shù) callback。這樣我們在編寫異步處理函數(shù)時,就可以調(diào)用 callback 函數(shù)來進行一些收尾的工作,而無需事先知道這些收尾的工作是什么。
偽代碼表示如下:
//異步處理函數(shù) void asynProcess(Method callback) { //執(zhí)行異步任務(wù) callback(); } //由調(diào)用者實現(xiàn)具體的回調(diào)函數(shù) void callback() { //異步處理完成后要進行的操作 }
三、用接口代替函數(shù)回調(diào)
上面我們提到,之所以使用函數(shù)回調(diào)這一方式,是因為 事先不知道某些功能的具體實現(xiàn),因此將具體實現(xiàn)留給其他開發(fā)者完成。
有沒有覺得這句話仿佛在描述 Java 的接口?接口(interface)是一組方法的抽象定義,具體實現(xiàn)由實現(xiàn)該接口的類來完成。
所以,利用面向?qū)ο蠛徒涌谶@兩個特性,可以代替函數(shù)回調(diào)。
我們以上面舉的兩個例子來說明接口是如何代替函數(shù)回調(diào)的。
排序函數(shù)
用接口實現(xiàn)排序函數(shù),不再要求開發(fā)者在使用該排序函數(shù)時提供回調(diào)函數(shù) compare,而是要求開發(fā)者確保待排序元素實現(xiàn)了 Comparable 接口,基于“待排序元素已經(jīng)實現(xiàn)了 Comparable 接口“這一前提下,我們無需知道待排序元素的類型,就可以實現(xiàn)排序功能。
//通用的排序函數(shù) void sort(Object[] array) { //利用 Comparable 接口的 compareTo 方法 //比較元素的大小,以便對 array 進行排序。 } //由排序函數(shù)定義的接口 public interface Comparable { public int compareTo(Object other); } //由調(diào)用者實現(xiàn) Comparable 接口 public class Element implements Comparable { @Override public int compareTo(Object other) { //判斷當(dāng)前 Element 與 other 的大小關(guān)系 //并返回兩者的關(guān)系 } }
異步處理函數(shù)
使用接口來實現(xiàn)異步處理函數(shù)時,不要求開發(fā)者提供回調(diào)函數(shù) callback,而是要求提供一個實現(xiàn)了指定接口的對象,這很好地體現(xiàn)了 Java 面向?qū)ο蟮乃枷?。相比提供一個函數(shù),一個對象包含的信息更豐富,使用起來更加靈活。但本質(zhì)上,該異步處理函數(shù)還是利用接口來完成收尾工作的。
//異步處理函數(shù) void asynProcess(ActionListener al) { //執(zhí)行異步任務(wù) al.actionPerformed(); } //由異步處理函數(shù)定義的接口 public interface ActionListener { void actionPerformed(); } //由調(diào)用者實現(xiàn) ActionListener 接口 public class ExtraTask implements ActionListener { @Override public void actionPerformed() { //異步處理函數(shù)執(zhí)行完成時,需要進行的額外工作 } } //調(diào)用異步處理函數(shù) public static void main(String[] args) { asynProcess(new ExtraTask()); }
四、總結(jié)
回調(diào)方式可以總結(jié)為:實現(xiàn)一個通用函數(shù) func,在具體場景中調(diào)用這個通用函數(shù)時,調(diào)用者需要提供合適的回調(diào)函數(shù) callback。通用函數(shù) func 利用該回調(diào)函數(shù),完成具體場景中的任務(wù)。
而接口實現(xiàn)的方式則是:實現(xiàn)一個通用函數(shù) func,在具體場景中調(diào)用這個通用函數(shù)時,被操作的對象需要自己實現(xiàn)合適的接口,通用函數(shù)會利用該接口,完成具體場景中的任務(wù)。
利用函數(shù)回調(diào)或者接口,都可以解決事先不知道具體實現(xiàn)的情況。函數(shù)回調(diào)方式傳遞的是函數(shù),而接口方式傳遞的是實現(xiàn)了該接口的對象。
在 Java 中,函數(shù)回調(diào)需要利用反射機制來完成,易出錯、效率低,而使用接口可以讓代碼的邏輯更加清晰、運行效率更高、也更便于調(diào)試。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Intellij IDEA 最全超實用快捷鍵整理(長期更新)
這篇文章主要介紹了Intellij IDEA 最全實用快捷鍵整理(長期更新),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02SpringBoot如何配置文件properties和yml
這篇文章主要介紹了SpringBoot如何配置文件properties和yml問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Session過期后自動跳轉(zhuǎn)到登錄頁面的實例代碼
這篇文章主要介紹了Session過期后自動跳轉(zhuǎn)到登錄頁面實例代碼,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-06-06Spring中的NamespaceHandler與BeanDefinitionParser詳解
這篇文章主要介紹了Spring中的NamespaceHandler與BeanDefinitionParser詳解,由Spring啟動過程之obtainFreshBeanFactory() ,可以看到NamespaceHandler、BeanDefinitionParser為解析配置文件中的Element起重要作用,那么它本身是如何被加載的呢,需要的朋友可以參考下2023-12-12JAVA生成八位不重復(fù)隨機數(shù)最快的方法總結(jié)(省時間省空間)
隨機數(shù)在實際中使用很廣泛,比如要隨即生成一個固定長度的字符串、數(shù)字,這篇文章主要給大家介紹了關(guān)于JAVA生成八位不重復(fù)隨機數(shù)最快的方法,文中介紹的方法省時間省空間,需要的朋友可以參考下2024-03-03解決idea中Terminal終端無法執(zhí)行GIT命令+Terminal 中文亂碼問題
這篇文章主要介紹了解決idea中Terminal終端無法執(zhí)行GIT命令+Terminal 中文亂碼問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07