優(yōu)雅地在Java 8中處理異常的方法詳解
前言
Java 8 引入的流 (Stream) API 和 lambda 表達(dá)式為我們打開了新世界的大門,自此之后我們也可以在 Java 中進(jìn)行函數(shù)式編程了。然而,在實(shí)際工作中,許多小伙伴并不知道如何正確的在 lambda 中處理異常,今天就來給大家講解一下。
小編給大家推薦一個(gè)Java技術(shù)交流群:937053620!群內(nèi)提供設(shè)計(jì)模式、spring/mybatis源碼分析、高并發(fā)與分布式、微服務(wù)、性能優(yōu)化,面試題整合文檔等免費(fèi)資料!給大家提供一個(gè)交流學(xué)習(xí)的平臺!
我們都知道,Java 異常分為檢查異常和非檢查異常。檢查異常就是編譯器要求開發(fā)者必須處理的異常,而非檢查異常則沒有這個(gè)要求。所以當(dāng)我們需要調(diào)用某個(gè)拋出檢查異常的方法時(shí),必須明確捕獲它:
myList.stream() .map(item -> try{ return doSomething(item); } catch(MyException e){ throw new RuntimeException (e); } }) .forEach(System.out::printion);
如上面代碼所示,我們捕獲了 MyException 這個(gè)檢查異常,然后將其轉(zhuǎn)化為 RuntimeException 非檢查異常,重新拋出。但是你自己心里面其實(shí)清楚的很,這不是最好的處理方式。
優(yōu)化一: 提升可讀性
如下所示,我們將方法體單獨(dú)提取到 trySomething 方法中,這樣的話,我們就可以使用一行代碼完成 lambda 表達(dá)式,整個(gè)代碼可讀性也會提升不少:
myList.stream() .map(this::trySomething) .forEach(System.out::printion); private Item trySomething(Item item) { try{ return doSomething(item); } catch(MyException e){ throw new RuntimeException (e); } }
優(yōu)化二: 復(fù)用代碼
現(xiàn)在你已經(jīng)解決了上述的問題,然而當(dāng)我們再碰到需要處理異常的其它方法時(shí),難道我們都要用 try ... catch ... 包裹一層嗎?那樣的話,你可以想象代碼中可能到處都是這種類似的寫法。為了避免陷入到這種重復(fù)的寫法中,我們應(yīng)該將上述代碼片段抽象為一個(gè)小的工具類,專門用來干這件事情。你只需要定義一次,然后再需要的地方多次調(diào)用它就可以了。
為了實(shí)現(xiàn)這個(gè)目標(biāo),我們首先需要自己定義一個(gè)函數(shù)式接口,這個(gè)接口可能會拋出一個(gè)異常:
然后,我們來寫一個(gè)靜態(tài)幫助函數(shù) wrap ,該方法接受一個(gè)函數(shù)式接口參數(shù),在方法體內(nèi)捕獲檢查異常,并拋出非檢查異常 RuntimeException:
借助于 wrap 靜態(tài)函數(shù),現(xiàn)在你可以在 lambda 表達(dá)式中這么寫了
優(yōu)化三: 出現(xiàn)異常時(shí)繼續(xù)運(yùn)行
上述代碼的可讀性、抽象性已經(jīng)很好了,然而還存在一個(gè)比較大的問題,那就是當(dāng)出現(xiàn)異常的時(shí)候,你的 stream 代碼會立即停止,不會接著處理下一個(gè)元素。大多數(shù)情況下,當(dāng)拋出異常的時(shí)候,我們可能還想讓 stream 繼續(xù)運(yùn)行下去。
我們與其拋出異常,將異常當(dāng)成一種特殊的情況處理,還不如直接將異常當(dāng)成是一個(gè) “正?!?的返回值。即這個(gè)函數(shù)要么返回一個(gè)正確的結(jié)果,要么返回一個(gè)異常,所以我們現(xiàn)在需要定義一個(gè)新的封裝類 Either,用來存儲這兩種結(jié)果。為了方便,我們將異常存儲到 left 這個(gè)字段中,將正常返回的值存儲到 right 這個(gè)字段中。下面就是 Either 類的一個(gè)簡單示例:
public class Eithercl<L,R>{ private final L Left: private final R right; private Either(L left, R right){ this left=left; this right =right; } public static <L, R> Either,<L,R> Left( L value) { return new Either(value, null): } public static <L, R> Either<L, R> Right( R value) { return new Either(null, value) } public Optional<L> getleft() { return Optional. ofnullable(left) } public Optional<R> getright() { return Optional.ofnullable(right); } public boolean isleft() { return left I- null; } public boolean isright(){ return right != null; } public < T> optional<T> mapleft(Function<? super L, T> mapper){ if (isleft()) { return Optional of(mapper. apply(left)); } return Optional empty(); } public <T> Optional<T> mapright(Function<? super R, T> mapper) { if (isright()) { return Optional of(mapper. apply(right)); } return Optionalempty(); } public String tostring(){ if (isleft()){ return"Left(”+left+")"; } return "Right("+ right +")"; } }
現(xiàn)在我們需要再定義一個(gè) lift 函數(shù),該函數(shù)內(nèi)部將 function 函數(shù)正常返回的值或者拋出的異常都使用 Either 類進(jìn)行了一層封裝
現(xiàn)在我們的代碼變成這個(gè)樣子了,也不用擔(dān)心方法拋出異常會提前終止 Stream 了
優(yōu)化四: 保留原始值
現(xiàn)在思考一個(gè)問題,如果在上述處理過程中,當(dāng)結(jié)果是異常信息的時(shí)候,我們想要重試,即重新調(diào)用這個(gè)方法怎么辦? 你會發(fā)現(xiàn)我們 Either 封裝類沒有保存最原始的這個(gè)值,我們丟掉了原始值,因此我們可以進(jìn)一步優(yōu)化,將原始值 t 也封裝進(jìn) left 字段中,就像下面這樣:
Pair 類是一個(gè)非常簡單的封裝類,用以封裝兩個(gè)值:
public class Pair<F, S> { public final F fst; public final S snd; private Pair(F fst, S snd){ this fst fst; this snd= snd; } public static <F, S> Pair<F, S> of(F fst, S snd){ return new Pair<>(fst, snd); } }
這樣,當(dāng)我們遇見異常的時(shí)候,我們可以從 Pair 中取出最原始的值 t,無論是想重試,還是做一些其他操作,都很方便了。
總結(jié)
我們經(jīng)過上文一點(diǎn)一點(diǎn)地優(yōu)化代碼,得到了一個(gè)比較滿意的在 Java 8 中處理異常的通用方式。其實(shí),大家還可以關(guān)注 Github 上的有關(guān)函數(shù)式編程方面的庫,比如 Javaslang ,它實(shí)現(xiàn)了多種多樣的函數(shù)式幫助方法和封裝類來幫助開發(fā)者寫好 lambda 表達(dá)式。但是,如果你只是為了處理異常,而引入這么大的一個(gè)第三方庫的話,就不太建議了哦~
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳
這篇文章主要介紹了關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳,File?和?IO?流其實(shí)是很相似的,都是將文件從一個(gè)地方轉(zhuǎn)移到另一個(gè)地方,這也是流的特點(diǎn)之一,需要的朋友可以參考下2023-04-04關(guān)于JVM垃圾回收的java.lang.ref.Finalizer問題
這篇文章主要介紹了關(guān)于JVM垃圾回收的java.lang.ref.Finalizer問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05ThreadLocal導(dǎo)致JVM內(nèi)存泄漏原因探究
ThreadLocal是JDK提供的線程本地變量機(jī)制,但若使用不當(dāng)可能導(dǎo)致內(nèi)存泄漏。正確的使用方式是在使用完后及時(shí)remove,或者使用弱引用等手段避免強(qiáng)引用導(dǎo)致的內(nèi)存泄漏。在多線程編程中,合理使用ThreadLocal可以提高并發(fā)性能,但也需要注意其潛在的內(nèi)存泄漏問題2023-04-04Java日期工具類操作字符串Date和LocalDate互轉(zhuǎn)
這篇文章主要介紹了Java日期工具類操作字符串Date和LocalDate互轉(zhuǎn),文章首先通過需要先引入坐標(biāo)展開主題的相關(guān)內(nèi)容介紹,需要的朋友可以參一下2022-06-06Spring?Cloud?+?Nacos?+?Seata整合過程(分布式事務(wù)解決方案)
Seata 是一款開源的分布式事務(wù)解決方案,致力于在微服務(wù)架構(gòu)下提供高性能和簡單易用的分布式事務(wù)服務(wù),這篇文章主要介紹了Spring?Cloud?+?Nacos?+?Seata整合過程(分布式事務(wù)解決方案),需要的朋友可以參考下2022-03-03