首頁 > 軟體

Java 回撥callback舉例詳解

2022-09-14 22:05:54

前言

回撥的核心就是回撥方將本身即this傳遞給呼叫方,這樣呼叫方就可以在呼叫完畢之後告訴回撥方它想要知道的資訊。

1、什麼是回撥

軟體模組之間總是存在一定的介面,從呼叫方式上,可以把他們分為三類:同步呼叫、回撥和非同步呼叫。

(1)同步呼叫:

同步呼叫是最基本並且最簡單的一種呼叫方式,類A的方法a()呼叫類B的方法b(),一直等待b()方法執行完畢,a()方法再繼續往下走。這種呼叫方式適用於方法b()執行時間不長的情況,因為b()方法執行時間一長或者直接阻塞的話,a()方法的餘下程式碼是無法執行下去的,這樣會造成整個流程的阻塞。

(2)非同步呼叫:

是一種類似訊息或事件的機制,是為了解決同步呼叫可能出現阻塞,導致整個流程卡住而產生的一種呼叫方式。類A的方法a()通過新起執行緒的方式呼叫類B的方法b(),程式碼接著直接往下執行,這樣無論方法b()執行時間多久,都不會阻塞方法a()的執行。但是這種方式,由於方法a()不等待方法b()執行完成,在方法a()需要方法b()執行結果的情況下(視具體業務而定,有些業務比如啟動非同步執行緒發個微信通知、重新整理一個快取這種就沒有必要),必須通過一定的方法對方法b()的執行結果進行監聽。在Java中,可以使用Future+Callable的方式做到這一點。

(3)回撥:

最後是回撥,回撥的思想是:

類A的a()方法呼叫了類B的b()方法類B的b方法執行完畢主動呼叫類A的callback()方法

這樣一種呼叫方式組成了上圖,也就是一種雙向的呼叫方式

回撥函數是一個函數或過程,不過它是一個由呼叫方自己實現,供被呼叫方使用的特殊函數。

在物件導向的語言中,回撥則是通過介面或抽象類來實現的,我們把實現這種介面的類稱為回撥類,回撥類的物件稱為回撥物件。

2、例子

開始之前。先想象一個場景:幼稚園的小朋友剛剛學習了10以內的加法。

第一章.故事的緣起

幼師在黑板上寫一個式子 “1+1=”,由小明來填空由於已經學習了10以內的加法,小明同學可以完全靠自己來計算這個題目,模擬該過程的程式碼如下:

public class Student
{
	private String name=null;
	public Student(String name)
	{
		this.name=name;
	}
	public void setName(String name)
	{
		this.name=name;
	}
	private int calcADD(int a,int b)
	{
		return a+b;
	}
	public void fillBlank(int a,int b)
	{
		int result=calcADD(a,b);
		System.out.println(name+"心算:"+a+"+"+b+"="+result);
	}
}

小明同學在填空(fillBlank)的時候,直接心算(clacADD)了一下,得出結果是2,並將結果寫在空格里。

測試程式碼如下:

public class Test
{
	public static void main(String[] args)
	{
		int a=1;
		int b=1;
		Student s=new Student("小明");
		s.fillBlank(a,b);
	}
}

執行結果如下:

小明心算:1+1=2

該過程完全由Student類的範例物件單獨完成,並未涉及回撥機制。

第二章.幼師的找茬

課間,幼師突發奇想在黑板上寫了"168+291=",讓小明完成,然後回辦公室去了。
這時候小明明顯不能再像上面那樣靠心算來完成了,正在懵逼的時候,班上的小紅同學遞過來一個只能計算加法的計算機,而小明同學恰好知道怎麼用計算器,於是通過計算器計算得到結果並完成了填空。

計算器的程式碼為:

public class Calculator
{
	public int add(int a,int b)
	{
		return a+b;
	}
}

修改Student類,新增使用計算器的方法:

public class Student
{
	private String name=null;
	public Student(String name)
	{
		this.name=name;
	}
	public void setName(String name)
	{
		this.name=name;
	}
	private int calcADD(int a,int b)
	{
		return a+b;
	}
	private int useCalculator(int a,int b)
	{
		return new Calculator().add(a,b);
	}
	public void fillBlank(int a,int b)
	{
		int result=useCalculator(a,b);
		System.out.println(name+"使用計算器:"+a+"+"+b+"="+result);
	}
}

測試程式碼如下:

public class Test
{
	public static void main(String[] args)
	{
		int a=168;
		int b=291;
		Student s=new Student("小明");
		s.fillBlank(a,b);
	}
}

執行結果如下:

小明使用計算器:168+291=459

該過程中仍未涉及到回撥機制,但是小明的部分工作已經實現了轉移,由計算器來協助實現。

第三章.幼師回來了

發現小明完成了3位數的加法,老師覺得小明很聰明,是個可塑之才。於是又在黑板上寫下了"26549+16387=",讓小明上課之前完成填空,然後又回辦公室了。
小明看著小紅再一次遞上來的計算機,心生一計:讓小紅代勞。
小明告訴小紅題目是"26549+16487=",然後指出填寫結果的具體位置,然後就出去快樂的玩耍了。
這裡,不把小紅單獨實現出來,而是把這個只能算加法的計算器和小紅看成一個整體,一個會算結果還會填空的超級計算器。折這個超級計算器需要傳的引數是兩個加數和要填空的位置,而這些內容需要小明提前告知,也就是小明要把自己的一部分方法暴露給小紅,最簡單的方法就是把自己的參照和兩個加數一塊告訴小紅。因此,超級計算器的add方法應該包含兩個運算元和小明自身的參照,

程式碼如下:

public class SuperCalculator
{
	public void add(int a,int b,Student xiaoming)
	{
		int result=a+b;
		xiaoming.fillBlank(a,b,result);
	}
}

小明這邊現在已經不需要心算,也不需要使用計算器,因此只需要有一個方法可以向小紅尋求幫助就行了,

程式碼如下:

public class Student
{
	private String name=null;
	public Student(String name)
	{
		this.name=name;
	}
	public void setName(String name)
	{
		this.name=name;
	}
	public void callHelp(int a,int b)
	{
		new SuperCalculator().add(a,b,this);
	}
	public void fillBlank(int a,int b,int result)
	{
		System.out.println(name+"求助小紅計算:"+a+"+"+b+"="+result);
	}
}

測試程式碼如下:

public class Test
{
	public static void main(String[] args)
	{
		int a=26549;
		int b=16487;
		Student s=new Student("小明");
		s.callHelp(a,b);
	}
}

執行結果為:

小明求助小紅計算:26549+16487=43036

執行流程為:小明通過自身的callHelp方法呼叫了小紅(new SuperCalculator())的add方法,在呼叫的時候將自身的參照(this)當作引數一併傳入,小紅在使用計算器得出結果之後,回撥了小明的fillBlank方法,將結果填在了黑板的空格上。

到這裡,回撥功能就正式登場了,小明的fillBlank方法就是我們常說的回撥函數。

通過這種方式,可以明顯的看出,對於完成老師的填空題這個問題上,小明已經不需要等待到加法做完且結果填寫在黑板上才能去跟小夥伴撒歡了,填空這個工作由超級計算器小紅來做了。回撥的優勢已經開始體現了。

第四章.門口的婆婆

幼稚園的門口有一個頭發花白的老婆婆,每天風雨無阻在那裡擺著地攤賣一些快過期的垃圾食品。由於年紀大了,腦子有些糊塗,經常算不清楚自己掙了多少錢。有一天,她無意間聽到了小明跟小夥伴們吹噓自己如何在小紅的幫助下與幼師鬥智鬥勇。於是,婆婆決定找到小紅牌超級計算器來做自己的小幫手,並提供一包衛龍辣條作為報酬。小紅經不住誘惑,答應了。

回看一下上一章的程式碼,我們發現小紅牌超級計算器的add方法需要的引數是兩個整型變數和一個Student物件,但是老婆婆她不是學生,是個小商販啊,這裡肯定要做修改。這種情況下,我們很自然的會想到繼承和多型。如果讓小明這個學生和老婆婆這個小商販從一個父類別進行繼承,那麼我們只需要給小紅牌超級計算器傳入一個父類別的參照就可以啦。

不過,實際使用中,考慮到java的單繼承,以及不希望把自身太多東西暴漏給別人,這裡使用從介面繼承的方式配合內部類來做。

換句話說,小紅希望以後繼續向班裡的小朋友們提供計算服務,同時還能向老婆婆提供算賬服務,甚至以後能夠拓展其他人的業務,於是她向所有的顧客約定了一個辦法,用於統一的處理,也就是自己需要的運算元和做完計算之後應該怎麼做。這個統一的方法,小紅做成了一個介面,提供給了大家,

程式碼如下:

public interface doJob
{
	public void fillBlank(int a,int b,int result);
}

因為靈感來自幫小明填空,因此小紅保留了初心,把所有業務當做填空(fillBlank)來做。

同時,小紅修改了自己的計算器,使其可以同時處理不同的實現了doJob介面的人,程式碼如下:

public class SuperCalulator
{
	public void add(int a,int b doJob customer)
	{
		int result=a+b;
		customer.fillBlank(a,b,result);
	}
}

小明和老婆婆拿到這個介面之後,只要實現了這個介面,就相當於按照統一的模式告訴小紅得到結果之後的處理辦法,按照之前說的使用內部類來做,程式碼如下:

小明的:

public class Student
{
	private String name=null;
	public Student(String name)
	{
		this.name=name;
	}
	public class doHomeWork implements doJob
	{
		@Override
		public void fillBlank(int a,int b,int result)
		{
			System.out.println(name+"求助小紅計算:"+a+"+"+b+"="+result);
		}
	}
	public void callHelp(int a,int b)
	{
		new SuperCalculator().add(a,b,new doHomeWork());
	}
}

老婆婆的:

public class Seller
{
	private String name=null;
	public Seller(String name)
	{
		this.name=name;
	}
	public setName(String name)
	{
		this.name=name;
	}
	public class doHomeWork implements doJob
	{
		@Override
		public void fillBlank(int a,int b,int result)
		{
			System.out.println(name+"求助小紅算賬:"+a+"+"+b+"="+result+"元";)
		}
	}
	public void callHelp(int a,int b)
	{
		new SuperCalculator().add(a,b,new doHomeWork());
	}
}

測試程式如下:

public class Test
{
	public static void main(String[] args)
	{
		int a=56;
		int b=31;
		int c=26497;
		int d=11256;
		Student s1=new Student("小明");
		Seller s2=new Seller("老婆婆");

		s1.callHelp(a,b);
		s2.callHelp(c,d);
	}
}

執行結果如下:

小明求助小紅計算:56+31=87
老婆婆求助小紅算賬:26497+11256=37753元

到此這篇關於Java 回撥callback舉例詳解的文章就介紹到這了,更多相關Java 回撥內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com