Java中使用回調函數的方法實例
背景
在Java中一個回調的操作是一個在一些操作完成之后被傳遞到另一個函數中并且被執(zhí)行的函數。一個回調函數既可以被同步或者異步執(zhí)行。在一個同步回調函數的案例中,一個函數緊著著另一個函數完成后被執(zhí)行。在一個異步回調函數的案例中,一個函數不需要在其他函數執(zhí)行的過程中按照特定順序時間內被執(zhí)行。
從在經典的監(jiān)視者設計模式中使用的監(jiān)聽案例開始,這篇文章向你介紹了Java中的回調函數。你將會看到大量的同步和異步的回調實現,包含使用 CompletableFuture類的函數式回調。
Java中的同步回調
一個同步回調函數總是會在一些操作執(zhí)行后立刻被執(zhí)行。這意味著它將會在動作被執(zhí)行后執(zhí)行。
正如我提到的,在一個監(jiān)視者設計模式中使用的一個回調函數的案例。在頁面UI的按鈕中,需要一個按鈕被點擊后去實例化一個操作,我們可以傳遞回調函數作為這個按鈕動作的監(jiān)聽器。這個監(jiān)聽函數會一直等待按鈕被點擊,直到按鈕被觸發(fā)后執(zhí)行。
現在讓我們看代碼中一個新的回調概念的案例
匿名內部類中的回調
任何時間我們在Java中,傳遞一個實現了接口的方法到另一個方法中,我們就是使用了回調函數的概念。在下面的代碼中,我們傳遞了一個Consumer函數接口和一個匿名內部類去實現accept()方法。
一旦 accept() 方法被實現,我們將會在performAction方法里面執(zhí)行它;然后我們會在Consumer接口里面執(zhí)行accept() 方法。
import java.util.function.Consumer; public class AnonymousClassCallback { ? public static void main(String[] args) { ? ? performAction(new Consumer<String>() { ? ? ? @Override ? ? ? public void accept(String s) { ? ? ? ? System.out.println(s); ? ? ? } ? ? }); ? } ? public static void performAction(Consumer<String> consumer) { ? ? System.out.println("Action is being performed..."); ? ? consumer.accept("Callback is executed"); ? } }
該代碼的輸出語句是:
Action is being performed…
Callback is executed…
在以上代碼中,我們將Consumer接口傳遞到performAction() 方法中,然后performAction()方法執(zhí)行后,調用accept()方法。
你可能注意到,使用一個匿名內部類是相當的啰嗦。使用lambda表達式替代匿名內部類將會更簡單。讓我們看看當我們在回調函數中使用lambda表達式會發(fā)生什么。
lambda的回調
在Java中,我們可以傳遞使用了lambda表達式實現的函數式接口到一個方法中,然后在一個操作結束后被執(zhí)行。在代碼中看起開是那樣。
public class LambdaCallback { ? public static void main(String[] args) { ? ? performAction(() -> System.out.println("Callback function executed...")); ? } ? public static void performAction(Runnable runnable) { ? ? System.out.println("Action is being performed..."); ? ? runnable.run(); ? } }
輸出結果表明,一旦這個操作執(zhí)行后,回調即被調用。
在這個例子中,你可能注意到我們在performAction方法中傳遞了可運行的函數式接口。因此,我們可以重寫run()方法并在performAction方法結束后執(zhí)行run()方法。
異步回調函數
我們經常想去使用一個異步回調方法,這意味著一個方法將會在操作執(zhí)行時被其他進程異步地調用。當一個回調方法不需要被其他進程立刻調用的時候,它可能對性能有所幫助。
簡單線程回調
讓我們以最簡單的異步回調操作方式開始。在下面的代碼中,首先我們從一個可執(zhí)行的函數式接口中實現了run()方法。然后我們在一個創(chuàng)建線程中使用了它。最終,我們將會啟動這個線程去異步地執(zhí)行這個已實現的可執(zhí)行函數式接口:
public class AsynchronousCallback { ? public static void main(String[] args) { ? ? Runnable runnable = () -> System.out.println("Callback executed..."); ? ? AsynchronousCallback asynchronousCallback = new AsynchronousCallback(); ? ? asynchronousCallback.performAsynchronousAction(runnable); ? } ? public void performAsynchronousAction(Runnable runnable) { ? ? new Thread(() -> { ? ? ? System.out.println("Processing Asynchronous Task..."); ? ? ? runnable.run(); ? ? }).start(); ? } }
這個案例的輸出結果是:
Processing Asynchronous Task…
Callback executed…
從上面的代碼中,我們創(chuàng)建了一個實現run()方法的Runnable,然后我們調用performAsynchronousAction()方法,傳遞了Runnable,在新建的線程中run()方法調用了它。
在performAsynchronousAction() 方法內,我們向它傳遞了一個runnable參數,在方法內部用lambda實現了一個線程的run方法調用了它.。先打印 “Processing Asynchronous Task…” 最終方法內部調用了以參數傳進來的runnable方法,打印 “Callback executed…”
平行執(zhí)行的異步回調
比起在異步調用中回調函數,我們也可以從其他函數中平行地調用一個回調函數。這意味著我們可以開啟兩個線程,平行地調用這兩個函數。
以下代碼和之前的有點像,但是要注意到除了直接調用回調函數外,我們可以開啟一個新的線程調用回調函數:
// Omitted code from above… public void performAsynchronousAction(Runnable runnable) { ? ? new Thread(() -> { ? ? ? System.out.println("Processing Asynchronous Task..."); ? ? ? new Thread(runnable).start(); ? ? }).start(); ? }
輸出結果是:
Processing Asynchronous Task…
Callback executed…
當我們不需要回調函數在performAsynchronousAction() 方法調用之后被立刻執(zhí)行,異步平行調用回調函數是非常有用的。
一個真實世界的案例,當我們在線上購買一個產品,我們不需要等待付款被確認后才執(zhí)行檢查庫存等所有的這些重度操作。在這個案例中,回調在后臺執(zhí)行的同時我們可以做其他事情。
CompletableFuture中的回調
使用異步回調函數的另一種方法是使用CompletableFuture API。Java8中引入的這個強大的API有助于執(zhí)行和組合異步方法調用。它完成了我們在上一個示例中所做的一切,例如創(chuàng)建一個新的線程,然后啟動和管理它。
在下面的代碼示例中,我們將創(chuàng)建一個新的CompletableFuture,然后調用傳遞String的supplyAsync方法。
接下來,我們將創(chuàng)建另一個CompletableFuture,它將應用回調函數來執(zhí)行我們配置的第一個函數:
import java.util.concurrent.CompletableFuture; public class CompletableFutureCallback { ? public static void main(String[] args) throws Exception { ? ? CompletableFuture<String> completableFuture ? ? ? ? = CompletableFuture.supplyAsync(() -> "Supply Async..."); ? ? CompletableFuture<String> execution = completableFuture ? ? ? ? .thenApply(s -> s + " Callback executed..."); ? ? System.out.println(execution.get()); ? } }
它的輸出結果如下:
Supply Async… Callback executed…
結論
回調在軟件開發(fā)中到處使用,廣泛地使用在工具、設計模式和應用程序中。有時候我們使用它們的時候卻沒有注意到。
在java代碼中,我們通過大量常見的回調實現幫助我們解釋它們廣泛的用處。這里有一些回調的特征需要我們記?。?/p>
- 回調函數應該在執(zhí)行另一個操作時執(zhí)行,或者與該操作并行執(zhí)行。
- 回調函數可以被同步執(zhí)行,意味著它可以在其他操作執(zhí)行后沒有延遲地被立刻執(zhí)行。
- 回調函數可以是異步的,這意味著它可以在后臺執(zhí)行,并且可能需要一些時間才能執(zhí)行
- Observable設計模式使用回調來通知感興趣的實體何時發(fā)生了操作。
到此這篇關于Java中使用回調函數的方法實例的文章就介紹到這了,更多相關Java 回調函數內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring Framework遠程代碼執(zhí)行漏洞分析(最新漏洞)
Spring Framework 是一個開源應用框架,旨在降低應用程序開發(fā)的復雜度,它具有分層體系結構,允許用戶選擇組件,同時還為 J2EE 應用程序開發(fā)提供了一個有凝聚力的框架,對Spring遠程代碼執(zhí)行漏洞相關知識感興趣的朋友一起看看吧2022-04-04idea中增強for循環(huán)提示unexpected token問題
這篇文章主要介紹了idea中增強for循環(huán)提示unexpected token問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01MyBatis?Generator生成的$?sql是否存在注入風險詳解
這篇文章主要介紹了MyBatis?Generator生成的$?sql是否存在注入風險詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12