回调函数、Java接口回调 总结
回調(diào)函數(shù)
談到回調(diào),我們得先從回調(diào)函數(shù)說起,什么叫回調(diào)函數(shù)呢?
回調(diào)函數(shù)是什么?
?? 百度百科的解釋:回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用為調(diào)用它所指向的函數(shù)時(shí),我們就說這是回調(diào)函數(shù)?;卣{(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外一方調(diào)用的,用于對該事件或條件進(jìn)行響應(yīng)。
?? 接著,我們從下圖簡單說明一下回調(diào)函數(shù)。
?? 已知圖形上面三種模塊,此時(shí)標(biāo)號2能稱為回調(diào)函數(shù)嗎?
答案:不能,只有當(dāng)標(biāo)號2函數(shù)作為參數(shù)傳遞給標(biāo)號3函數(shù)使用時(shí),才能稱為回調(diào)函數(shù)。
再比如,人(類似函數(shù)聲明)、老王(類似函數(shù)定義)、學(xué)校(類似調(diào)用方)三個(gè)概念,某學(xué)校需要招聘人當(dāng)教師,這時(shí)老王去應(yīng)聘,由于老王具有出色的教導(dǎo)能力,學(xué)校聘用老王作為高級教師。被學(xué)校成功聘用的老王,此時(shí)才能稱為高級教師(類似回調(diào)函數(shù)),否則他還只是老王這一個(gè)身份,而不能稱為高級教師。
回調(diào)函數(shù)的機(jī)制:
(1)定義一個(gè)回調(diào)函數(shù);
(2)提供函數(shù)實(shí)現(xiàn)的一方在初始化時(shí)候,將回調(diào)函數(shù)的函數(shù)指針注冊給調(diào)用者;
(3)當(dāng)特定的事件或條件發(fā)生的時(shí)候,調(diào)用者使用函數(shù)指針調(diào)用回調(diào)函數(shù)對事件進(jìn)行處理。
回調(diào)函數(shù)通常與原始調(diào)用者處于同一層次,如圖所示:
?
為什么使用回調(diào)函數(shù)?
????? 因?yàn)榭梢园颜{(diào)用者與被調(diào)用者分開。調(diào)用者不關(guān)心誰是被調(diào)用者,所有它需知道的,只是存在一個(gè)具有某種特定原型、某些限制條件(如返回值為int)的被調(diào)用函數(shù)。
???? 如果想知道回調(diào)函數(shù)在實(shí)際中有什么作用,先假設(shè)有這樣一種情況,我們要編寫一個(gè)庫,它提供了某些排序算法的實(shí)現(xiàn),如冒泡排序、快速排序、shell排序、shake排序等等,但為使庫更加通用,不想在函數(shù)中嵌入排序邏輯,而讓使用者來實(shí)現(xiàn)相應(yīng)的邏輯;或者,想讓庫可用于多種數(shù)據(jù)類型(int、float、string),此時(shí),該怎么辦呢?可以使用函數(shù)指針,并進(jìn)行回調(diào)。
回調(diào)可用于通知機(jī)制,例如,有時(shí)要在程序中設(shè)置一個(gè)計(jì)時(shí)器,每到一定時(shí)間,程序會得到相應(yīng)的通知,但通知機(jī)制的實(shí)現(xiàn)者對我們的程序一無所知。而此時(shí),就需有一個(gè)特定原型的函數(shù)指針,用這個(gè)指針來進(jìn)行回調(diào),來通知我們的程序事件已經(jīng)發(fā)生。實(shí)際上,SetTimer() API使用了一個(gè)回調(diào)函數(shù)來通知計(jì)時(shí)器,而且,萬一沒有提供回調(diào)函數(shù),它還會把一個(gè)消息發(fā)往程序的消息隊(duì)列。
????? 另一個(gè)使用回調(diào)機(jī)制的API函數(shù)是EnumWindow(),它枚舉屏幕上所有的頂層窗口,為每個(gè)窗口調(diào)用一個(gè)程序提供的函數(shù),并傳遞窗口的處理程序。如果被調(diào)用者返回一個(gè)值,就繼續(xù)進(jìn)行迭代,否則,退出。EnumWindow()并不關(guān)心被調(diào)用者在何處,也不關(guān)心被調(diào)用者用它傳遞的處理程序做了什么,它只關(guān)心返回值,因?yàn)榛诜祷刂?#xff0c;它將繼續(xù)執(zhí)行或退出。
如何使用回調(diào)函數(shù)?
使用回調(diào)函數(shù),我們需要做三件事:
1、聲明函數(shù)模型
2、定義函數(shù)體
3、將回調(diào)函數(shù)作為參數(shù)傳遞給滿足格式的函數(shù),以便系統(tǒng)調(diào)用。
例1:一段C語言代碼
#include <iostream>using namespace std;
// 1、聲明
typedef void (*PF)();
// 2、定義
void func()
{
? cout << "func" << endl;
}
void caller( PF pf)
{
? pf();
}
int main()
{
? PF p = func;
? // 3、函數(shù)作為參數(shù)傳遞
? caller(p);
? system("pause");
? return 0;
}
例2:C#代碼 using System;
using System.Runtime.InteropServices;
// 1、函數(shù)聲明
public delegate bool CallBack(int hwnd, int lParam);
public?class EnumReportApp
{
??? [DllImport("user32")]
??? public?static extern int EnumWindows(CallBack x, int y);
??? public?static?void Main()
??? {
??????? // 3、函數(shù)作為參數(shù)傳遞
??????? CallBack myCallBack = new CallBack(EnumReportApp.Report);
??????? EnumWindows(myCallBack, 0);
??? }
??? // 2、函數(shù)定義
??? public?static bool Report(int hwnd, int lParam)
??? {
??????? Console.Write("Window handle is ");
??????? Console.WriteLine(hwnd);
??????? return true;
??? }
}
從回調(diào)函數(shù)的使用說明中,我們可以將分為兩部分:協(xié)議和協(xié)議的調(diào)用
1、協(xié)議規(guī)范(函數(shù)聲明)
2、協(xié)議實(shí)現(xiàn)(函數(shù)定義)
3、調(diào)用協(xié)議(函數(shù)作為參數(shù)傳遞,使用)
?
接口回調(diào)
Java是一門面向?qū)ο笳Z言,一切皆對象,因此在Java中不存在回調(diào)函數(shù)這一說法的。由于Java的一切皆對象性質(zhì),從而將回調(diào)函數(shù)這個(gè)特性提升到了接口回調(diào)。
接口回調(diào)是什么?
接口回調(diào):可以把使用某一接口的類創(chuàng)建的對象的引用賦給該接口聲明的接口變量,那么該接口變量就可以調(diào)用被類實(shí)現(xiàn)的接口的方法。實(shí)際上,當(dāng)接口變量調(diào)用被類實(shí)現(xiàn)的接口中的方法時(shí),就是通知相應(yīng)的對象調(diào)用接口的方法,這一過程稱為對象功能的接口回調(diào)。
從概念可以看出,接口回調(diào)是指一個(gè)使用過程,并強(qiáng)調(diào)是關(guān)于對象功能的使用過程,既然是功能,功能一般就對應(yīng)著方法體(函數(shù)),因此它同樣滿足與回調(diào)函數(shù)相似的模型。
(1)接口的定義
(2)接口的實(shí)現(xiàn)
(3)調(diào)用接口
將(2)的引用(地址)傳遞給(3),然后(3)調(diào)用(2)中的方法,這一過程稱為對象功能的接口回調(diào)。
?
接口回調(diào)與回調(diào)函數(shù)不同點(diǎn):接口回調(diào)注重的是過程,而回調(diào)函數(shù)強(qiáng)調(diào)的是函數(shù)(實(shí)體)。
接口回調(diào)與回調(diào)函數(shù)相同點(diǎn):都是將自身地址傳遞給調(diào)用者,讓調(diào)用者根據(jù)地址調(diào)用相關(guān)的方法。
關(guān)于回調(diào)的個(gè)人簡單理解就是:將你本身的地址傳給我,我根據(jù)你的地址去調(diào)用你。
接口回調(diào)的機(jī)制與回調(diào)函數(shù)的機(jī)制類似:
(1)定義一個(gè)接口;
(2)提供接口實(shí)現(xiàn)的一方在初始化的時(shí)候,將接口回調(diào)的引用注冊給調(diào)用者;
(3)當(dāng)特定的事件或條件發(fā)生的時(shí)候,調(diào)用者使用引用調(diào)用實(shí)現(xiàn)的接口方法對事件進(jìn)行處理。
接口回調(diào)的好處與回調(diào)函數(shù)的使用類似,在此就不重復(fù)介紹。
如何使用接口回調(diào)?
接口回調(diào),我將其分為兩種方式,一種推模式,一種為拉模式。
推模式
接口回調(diào)的推模式,指的是,甲方主動將其地址推送給調(diào)用者。比如下例:
接口:
public?interface GasListener{
??? public?void offerGas(String msg);
} 接口實(shí)現(xiàn)的甲方: public?class GasCompany implements GasListener
{???
??? public?void advertiseTo(IndoorsMan man)
??? {
??????? System.out.println("煤氣公司:這是我們的聯(lián)系方式,歡迎來電!");
??????? man.setListener(this);
??? }
??? @Override
??? public?void offerGas(String msg)
??? {
??????? System.out.println("煤氣公司接收的訂單:"+msg);
??? }
} 調(diào)用者:
public?class IndoorsMan
{
??? GasListener gListener;
??? public?void prepareCook()
??? {
??????? System.out.println("宅男:準(zhǔn)備下廚做幾個(gè)花式大菜!");
??????? System.out.println("宅男:進(jìn)廚房,燒菜...");
??????? System.out.println("宅男:剛開火,就發(fā)現(xiàn)煤氣不足,沒辦法,只能打電話叫煤氣。");
??????? gListener.offerGas("宅男:送一瓶煤氣過來!");
??? }???
??? public?void setListener(GasListener gListener)
??? {
??????? this.gListener = gListener;
??? }
} 測試:
public?class Test
{
??? public?static?void main(String[] args)
??? {
??????? IndoorsMan man = new IndoorsMan();
??????? GasCompany company = new GasCompany();
???????
??????? company.advertiseTo(man);
??????? man.prepareCook();
??? }???????????
} GasCompany公司在打廣告時(shí),就主動把自身信息告訴了IndoorsMan,當(dāng)IndoorsMan發(fā)現(xiàn)煤氣不足這一事件發(fā)生時(shí),IndoorsMan就根據(jù)接口的引用調(diào)用GasCompany服務(wù)。
拉模式 接口回調(diào)的拉模式,指的是,調(diào)用者自己主動獲取甲方的信息。
調(diào)用者
public?class IndoorsMan{
??? GasListener gListener;
??? public?void prepareCook()
??? {
??????? System.out.println("宅男:準(zhǔn)備下廚做幾個(gè)花式大菜!");
??????? System.out.println("宅男:進(jìn)廚房,燒菜...");
??????? System.out.println("宅男:剛開火,就發(fā)現(xiàn)煤氣不足,沒辦法,只能打電話叫煤氣。");
??????? gListener.offerGas("宅男:送一瓶煤氣過來!");
??? }???
??? public?void setListener(GasListener gListener)
??? {
??????? this.gListener = gListener;
??? }
??? public?void configureGas()
??? {
??????? // 主動獲取甲方信息
??????? setListener(new GasListener()
??????? {???????????
??????????? @Override
??????????? public?void offerGas(String msg)
??????????? {
??????????????? System.out.println("下單內(nèi)容:"+msg);
??????????? }
??????? });
??? }???
} 測試 public?class Test
{
??? public?static?void main(String[] args)
??? {
??????? IndoorsMan man = new IndoorsMan();
??????? man.configureGas();
??????? man.prepareCook();
??? }???????????
} 再比如,使用線程Thread時(shí),常常會在構(gòu)建線程對象時(shí),主動獲取Runnable接口的實(shí)現(xiàn)。 public?class Test
{
??? public?static?void main(String[] args)
??? {
??????? Thread thread = new Thread(new Runnable()
??????? {
??????????? @Override
??????????? public?void run()
??????????? {
???????????
??????????? }
??????? });
??? }???????????
} 擴(kuò)展: 接口回調(diào)機(jī)制滿足觀察者設(shè)計(jì)模式,觀察者將自身地址注冊給被觀察者,當(dāng)某一事件發(fā)生時(shí),被觀察者根據(jù)注冊的地址調(diào)用相應(yīng)的觀察者。
參考:
1、回調(diào)函數(shù)和函數(shù)指針
2、一個(gè)經(jīng)典例子讓你徹徹底底理解java回調(diào)機(jī)制
3、JAVA回調(diào)機(jī)制(CallBack)詳解
轉(zhuǎn)載于:https://www.cnblogs.com/aoguren/p/5544865.html
總結(jié)
以上是生活随笔為你收集整理的回调函数、Java接口回调 总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C语言】06-基本数据类型
- 下一篇: Java 分页通用