如果还不懂如何使用 Consumer 接口,就来看这篇!
作者 | 阿Q
來源 |?阿Q說代碼
背景
在開發過程中我遇到這么一個問題:
表結構:一張主表A ,一張關聯表B ,表 A 中存儲著表 B 記錄的狀態。
場景:第一步創建主表數據,插入A表;第二步調用第三方接口插入B表同時更新A表的狀態。此時大家應該都會想到在進行第二步的時候需要做好數據的冪等性。這樣的話就會存在以下幾種情況:
一、B表中不存在與A表關聯的數據,此時需要調用第三方接口,插入B表同時更新A表的狀態;
二、B表中存在與A表關聯的數據;
A表中的狀態為處理中:直接返回處理中字樣;
A表中的狀態為處理成功:直接返回成功的字樣;
A表中的狀態為處理失敗:此時需要調用第三方接口,更新B表同時更新A表的狀態;
代碼實現
首先我是這樣編寫的偽代碼
B?b?=?this.baseMapper.selectOne(queryWrapper); if?(b?!=?null)?{String?status?=?b.getStatus();if?(Objects.equals(Constants.STATUS_ING,?status)){return?"處理中";}?else?if?(Objects.equals(Constants.STATUS_SUCCESS,?status)){return?"處理成功";}//失敗的操作//請求第三方接口并解析響應結果......if?(ReturnInfoEnum.SUCCESS.getCode().equals(parse.getCode()))?{......//更新B表操作bb.setStatus(Constants.STATUS_ING);mapper.updateById(bb);//更新A表的狀態a.setStatus(Constants.STATUS_ING);aMapper.updateById(a);}}?else?{//請求第三方接口并解析響應結果......if?(ReturnInfoEnum.SUCCESS.getCode().equals(parse.getCode()))?{......//插入B表操作bb.setStatus(Constants.STATUS_ING);mapper.insert(bb);//更新A表的狀態a.setStatus(Constants.STATUS_ING);aMapper.updateById(a);} }不知道細心的小伙伴是否發現,存在B表記錄并且狀態為“失敗”的情況和不存在B表的情況除了插入B表或者更新B表的操作之外,其余的操作都是相同的。
如果我們想要將公共的部分抽取出來,發現都比較零散,還不如不抽取,但是不抽取代碼又存在大量重復的代碼不符合我的風格。于是我便將手伸向了 Consumer 接口。
更改之后的偽代碼
B?b?=?this.baseMapper.selectOne(queryWrapper); if?(b?!=?null)?{String?status?=?b.getStatus();if?(Objects.equals(Constants.STATUS_ING,?status)){return?"處理中";}?else?if?(Objects.equals(Constants.STATUS_SUCCESS,?status)){return?"處理成功";}//失敗的操作getResponse(dto,?response,?s?->?mapper.updateById(s)); }?else?{getResponse(dto,?response,?s?->?mapper.updateById(s)); }public?void?getResponse(DTO?dto,?Response?response,?Consumer<B>?consumer){//請求第三方接口并解析響應結果......if?(ReturnInfoEnum.SUCCESS.getCode().equals(parse.getCode()))?{......bb.setStatus(Constants.STATUS_ING);consumer.accept(bb);//更新A表的狀態a.setStatus(Constants.STATUS_ING);aMapper.updateById(a);} }看到這,如果大家都已經看懂了,那么恭喜你,說明你對 Consumer 的使用已經全部掌握了。如果你還存在一絲絲的疑慮,那么就接著往下看,我們將介紹一下四種常見的函數式接口。
函數式接口
那什么是函數式接口呢?函數式接口是只有一個抽象方法(Object的方法除外),但是可以有多個非抽象方法的接口,它表達的是一種邏輯上的單一功能。
@FunctionalInterface
@FunctionalInterface ?注解用來表示該接口是函數式接口。它有助于及早發現函數式接口中出現的或接口繼承的不適當的方法聲明。
如果接口用該注解來注釋,但實際上不是函數式接口,則會在編譯時報錯。
Consumer
我們一般稱之為“消費者”,它表示接受單個輸入參數但不返回結果的操作。不同于其它函數式接口,Consumer 預期通過副作用進行操作。
那什么又是副作用呢?說一下我所理解的副作用,副作用其實就是一個函數是否會修改它范圍之外的資源,如果有就叫有副作用,反之為沒有副作用。比如修改全局變量,修改輸入參數所引用的對象等。
@FunctionalInterface public?interface?Consumer<T>?{/***??對給定的參數執行此操作。*/void?accept(T?t);/***?*??返回一個組合的 Consumer ,依次執行此操作,然后執行after操作。*??如果執行任一操作會拋出異常,它將被轉發到組合操作的調用者。*??如果執行此操作會引發異常,則不會執行after操作。*/default?Consumer<T>?andThen(Consumer<??super?T>?after)?{Objects.requireNonNull(after);return?(T?t)?->?{?accept(t);?after.accept(t);?};} }正如我們案例中遇到的場景,我們只需要將要執行的邏輯方法當作參數傳入 getResponse() 中,然后在該方法中執行 accept() 方法進行消費即可。如果還不理解,我們可以把它轉換為匿名內部類的調用方式。
getResponse(dto,?response,?new?Consumer<B>()?{@Overridepublic?void?accept(B?bb)?{mapper.insert(bb);} });當調用accept() 方法的時候就會去調用匿名內部類的方法了,也就是我們傳入 getResponse() 的邏輯方法。
Supplier
我們一般稱之為“生產者”,沒有參數輸入,但是能返回結果,為結果的提供者。
@FunctionalInterface public?interface?Supplier<T>?{/***??獲取一個結果*/T?get(); }可以舉個簡單的例子感受下:
Optional<Double>?optional?=?Optional.empty(); optional.orElseGet(()->Math.random()?);//orElseGet?方法的源碼,里邊用到了?get?方法 public?T?orElseGet(Supplier<??extends?T>?other)?{???return?value?!=?null???value?:?other.get(); }Function
我把它稱為“轉換者”,表示接收一個參數通過處理之后返回一個結果的函數。
@FunctionalInterface public?interface?Function<T,?R>?{/***??將?T?類型的參數傳入,經過函數表達式的計算,返回?R?類型的結果*/R?apply(T?t);/***?返回一個組合函數,先將參數應用于 before 函數,然后將結果應用于當前函數,返回最終結果。*?如果對任一函數的求值引發異常,則會將其轉發給組合函數的調用方。*/default?<V>?Function<V,?R>?compose(Function<??super?V,???extends?T>?before)?{Objects.requireNonNull(before);return?(V?v)?->?apply(before.apply(v));}/***?返回一個組合函數,先將參數應用與當前函數,然后將結果應用于 after 函數,返回最終的結果。*?如果對任一函數的求值引發異常,則會將其轉發給組合函數的調用方。?*/default?<V>?Function<T,?V>?andThen(Function<??super?R,???extends?V>?after)?{Objects.requireNonNull(after);return?(T?t)?->?after.apply(apply(t));}/***??返回始終返回其輸入參數的函數。*/static?<T>?Function<T,?T>?identity()?{return?t?->?t;} }我們在 lambda 表達式中應用比較多,所以我們來簡單演示下:
@Data @AllArgsConstructor public?class?Teacher?{private?String?name;private?int?age; }public?class?TeacherTest?{public?static?void?main(String[]?args)?{List<Teacher>?list?=?Arrays.asList(new?Teacher("張三",25),new?Teacher("李四",28),new?Teacher("王五",18));List<String>?collect?=?list.stream().map(item?->?item.getName()).collect(Collectors.toList());System.out.println(collect);} }其中 map 接收的參數就是 Function 類型, item 為傳入參數,item.getName() ?為返回處理的結果,最后輸出結果為
[張三,?李四,?王五]Predicate
我們稱之為“判斷者”,通過接收參數 T 來返回 boolean 的結果。
@FunctionalInterface public?interface?Predicate<T>?{/***??接收一個參數,?判斷這個參數是否匹配某種規則,?匹配成功返回true,?匹配失敗則返回false*/boolean?test(T?t);/***??接收一個?Predicate?類型的參數,用當前函數和?other?函數邏輯與判斷參數?t?是否匹配規則,成功返回true,失敗返回?false?*??如果當前函數返回?false,則?other?函數不進行計算*?在評估?Predicate?期間引發的任何異常都會轉發給調用方*/default?Predicate<T>?and(Predicate<??super?T>?other)?{Objects.requireNonNull(other);return?(t)?->?test(t)?&&?other.test(t);}/***??返回當前Predicate取反操作之后的Predicate*/default?Predicate<T>?negate()?{return?(t)?->?!test(t);}/***??接收一個?Predicate?類型的參數,用當前函數和?other?函數?邏輯或?判斷參數?t?是否匹配規則,成功返回true,失敗返回?false?*??如果當前函數返回?true,則?other?函數不進行計算*?在評估?Predicate?期間引發的任何異常都會轉發給調用方*/default?Predicate<T>?or(Predicate<??super?T>?other)?{Objects.requireNonNull(other);return?(t)?->?test(t)?||?other.test(t);}/***??靜態方法:傳入一個參數,用來生成一個 Predicate,調用test()?方法時調的 object -> targetRef.equals(object)?函數式**/static?<T>?Predicate<T>?isEqual(Object?targetRef)?{return?(null?==?targetRef)??Objects::isNull:?object?->?targetRef.equals(object);} }相信大家在編碼過程中經常會遇到該函數式接口,我們舉個例子來說一下:
public?static?void?main(String[]?args)?{List<Teacher>?list?=?Arrays.asList(new?Teacher("張三",25),new?Teacher("李四",28),new?Teacher("王五",18));list?=?list.stream().filter(item?->?item.getAge()>25).collect(Collectors.toList());list.stream().forEach(item->System.out.println(item.getName())); }其中 filter() 的參數為 Predicate 類型的,返回結果為:李四
看到這兒,我們常見的四種函數式接口就已經介紹完了。說實話,函數式接口我已經看過好幾遍了,尤其是 Consumer 和 Supplier。當時只是腦子里學會了,沒有應用到具體的項目中,下次再遇到的時候還是一臉懵逼,不知道大家有沒有這種感受。
往期推薦
虛幻引擎5上的《黑客帝國》全新體驗,愛了愛了
元宇宙真的是割韭菜嗎?
Log4j 第三次發布漏洞補丁,漏洞或將長存
低代碼發展專訪系列之六:低代碼平臺能解決業務重構的問題嗎?
點分享
點收藏
點點贊
點在看
總結
以上是生活随笔為你收集整理的如果还不懂如何使用 Consumer 接口,就来看这篇!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器之后加码存储,浪潮信息重磅发布新一
- 下一篇: 国家能源集团携手华为首次部署矿鸿操作系统