摘要:如果的流式操作中多幾個需要拋出受檢異常的情況,那代碼真是太不直觀了,所以為了的,我們需要解決的辦法。不過既然受檢異常已經(jīng)是中的客觀存在的事物,所謂道高一尺,魔高一丈總是會有辦法來應(yīng)對。
我今天高高興興,想寫個簡單的統(tǒng)計一個項目下有多少行代碼的小程序,于是咔咔的寫下:
long count = Files.walk(Paths.get("D:/Test")) // 獲得項目目錄下的所有目錄及文件 .filter(file -> !Files.isDirectory(file)) // 篩選出文件 .filter(file -> file.toString().endsWith(".java")) // 篩選出 java 文件 .flatMap(file -> Files.lines(file)) // 按行獲得文件中的文本 .filter(line -> !line.trim().isEmpty()) // 過濾掉空行 .count(); System.out.println("代碼行數(shù):" + count);
{ 題外話開始:
Files.walk(Path) 在 JDK1.8 時添加,深度優(yōu)先遍歷一個 Path (目錄),返回這個目錄下所有的 Path(目錄和文件),通過 Stream
Files.lines(Path) 也是在 JDK1.8 時添加,功能是返回指定 Path (文件)中所有的行,通過 Stream
題外話結(jié)束 }
然后,編譯不過 —— 因為 Files.lines(Path) 會拋出 IOException,如果要編譯通過,得這樣寫:
long count = Files.walk(Paths.get("D:/Test")) // 獲得項目目錄下的所有文件 .filter(file -> !Files.isDirectory(file)) // 篩選出文件 .filter(file -> file.toString().endsWith(".java")) // 篩選出 java 文件 .flatMap(file -> { try { return Files.lines(file); } catch (IOException ex) { ex.printStackTrace(System.err); return Stream.empty(); // 拋出異常時返回一個空的 Stream } }) // 按行獲得文件中的文本 .filter(line -> !line.trim().isEmpty()) // 過濾掉空行 .count(); System.out.println("代碼行數(shù):" + count);
我的天,這個時候我強迫癥就犯了 —— 因為這樣的 Lambda 不是 one-liner expression,不夠簡潔。如果 Stream 的流式操作中多幾個需要拋出受檢異常的情況,那代碼真是太不直觀了,所以為了 one-liner expression 的 Lambda,我們需要解決的辦法。
解決方法1:通過新建一個方法(:) 無奈但是純潔的微笑)
public static void main(String[] args) throws Exception { long count = Files.walk(Paths.get("D:/Test")) // 獲得項目目錄下的所有文件 .filter(file -> !Files.isDirectory(file)) // 篩選出文件 .filter(file -> file.toString().endsWith(".java")) // 篩選出 java 文件 .flatMap(file -> getLines(file)) // 按行獲得文件中的文本 .filter(line -> !line.trim().isEmpty()) // 過濾掉空行 .count(); System.out.println("代碼行數(shù):" + count); } private static StreamgetLines(Path file) { try { return Files.lines(file); } catch (IOException ex) { ex.printStackTrace(System.err); return Stream.empty(); } }
這種解決方法下,我們需要處理受檢異常 —— 即在程序拋出異常的時候,我們需要告訴程序怎么去做(getLines 方法中拋出異常時我們輸出了異常,并返回一個空的 Stream)
解決方法2:將會拋出異常的函數(shù)進行包裝,使其不拋出受檢異常
如果一個 FunctionInterface 的方法會拋出受檢異常(比如 Exception),那么該 FunctionInterface 便可以作為會拋出受檢異常的 Lambda 的目標(biāo)類型。
我們定義如下一個 FunctionInterface:
@FunctionalInterface interface UncheckedFunction{ R apply(T t) throws Exception; }
那么該 FunctionInterface 便可以作為類似于 file -> File.lines(file) 這類會拋出受檢異常的 Lambda 的目標(biāo)類型,此時 Lambda 中并不需要捕獲異常(因為目標(biāo)類型的 apply 方法已經(jīng)將異常拋出了)—— 之所以原來的 Lambda 需要捕獲異常,就是因為在流式操作 flatMap 中使用的 java.util.function 包下的 Function
那我們?nèi)绾问褂?UncheckedFunction 到流式操作的 Lambda 中呢?
首先我們定義一個 Try 類,它的 of 方法提供將 UncheckedFunction 包裝為 Function 的功能:
public class Try { public staticFunction of(UncheckedFunction mapper) { Objects.requireNonNull(mapper); return t -> { try { return mapper.apply(t); } catch (Exception ex) { throw new RuntimeException(ex); } }; } @FunctionalInterface public static interface UncheckedFunction { R apply(T t) throws Exception; } }
然后在原先的代碼中,我們使用 Try.of 方法來對會拋出受檢異常的 Lambda 進行包裝:
long count = Files.walk(Paths.get("D:/Test")) // 獲得項目目錄下的所有文件 .filter(file -> !Files.isDirectory(file)) // 篩選出文件 .filter(file -> file.toString().endsWith(".java")) // 篩選出 java 文件 .flatMap(Try.of(file -> Files.lines(file))) // 將 會拋出受檢異常的 Lambda 包裝為 拋出非受檢異常的 Lambda .filter(line -> !line.trim().isEmpty()) // 過濾掉空行 .count(); System.out.println("代碼行數(shù):" + count);
此時,我們便可以選擇是否去捕獲異常(RuntimeException)。這種解決方法下,我們一般不關(guān)心拋出異常的情況 —— 比如自己寫的小例子,拋出了異常程序就該終止;或者你知道這個 Lambda 確實 100% 不會拋出異常。
我更傾向于一種指定默認(rèn)值的包裝方法,即如果拋出異常,那么就返回默認(rèn)值:
public staticFunction of( UncheckedFunction mapper, R defaultR) { Objects.requireNonNull(mapper); return t -> { try { return mapper.apply(t); } catch (Exception ex) { System.err.println(ex.getMessage()); return defaultR; } }; }
比如我們前面的例子,如果 file -> Files.lines(file) 拋出異常了,說明在訪問 file 類的時候出了問題,我們可以就假設(shè)這個文件的行數(shù)為 0 ,那么默認(rèn)值就是個空的 Stream
long count = Files.walk(Paths.get("D:/Test")) // 獲得項目目錄下的所有文件 .filter(file -> !Files.isDirectory(file)) // 篩選出文件 .filter(file -> file.toString().endsWith(".java")) // 篩選出 java 文件 .flatMap(Try.of(file -> Files.lines(file), Stream.empty())) .filter(line -> !line.trim().isEmpty()) // 過濾掉空行 .count(); System.out.println("代碼行數(shù):" + count);
使用 UncheckedFunction 這種方式更為通用,我們可以在更多的地方將 UncheckedFunction 包裝成 java.util.function.Function。類似的,我們可以包裝 UncheckedConsumer 為 java.util.function.Consumer,包裝 UncheckedSupplier 為 Suppiler,UncheckedBiFunction 為 BiFunction 等。
就我個人觀點而言,我真的不喜歡 Java 中的受檢(Checked)異常,我認(rèn)為所有的異常都應(yīng)該是非受檢(Unchecked)的 —— 因為一段代碼如果會產(chǎn)生異常,我們自然會去解決這個問題直到其不拋出異?;蛘卟东@這個異常并做對應(yīng)處理 —— 強制性的要求編碼人員捕獲異常,帶來的更多的是編碼上的不方便和代碼可讀性的降低(因為冗余)。不過既然受檢異常已經(jīng)是 Java 中的客觀存在的事物,所謂“道高一尺,魔高一丈” —— 總是會有辦法來應(yīng)對。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/66363.html
摘要:函數(shù)副作用會給程序設(shè)計帶來不必要的麻煩,引入潛在的,并降低程序的可讀性。所以只能采用這種曲線救國的方式。則是把這種曲線救國拿到了臺面上,并昭告天下,同時還對提供了一些語法支持。是自由變量,提供執(zhí)行上下文,觸發(fā)閉包執(zhí)行。 背景 自從2013年放棄了Java就再也沒有碰過。期間Java還發(fā)布了重大更新:引入lambda,但是那會兒我已經(jīng)玩了一段時間Scala,對Java已經(jīng)瞧不上眼。相比S...
摘要:解決思路或生產(chǎn)對象,扮演生產(chǎn)者的角色而消費對象,扮演消費者的角色。正常情況下它們生產(chǎn)對象,而異常情況下,則拋出異常。重構(gòu)的思路在于將異常處理更加明晰化,讓生產(chǎn)者與消費者之間的關(guān)系流水化。容器化其中,與包內(nèi)私有,對外不公開。 場景 以一個簡化了的用戶登錄的鑒權(quán)流程,流程大體如下: 首先嘗試本站鑒權(quán),如果失敗,再嘗試twiter的方式恢復(fù); 之后再進行Two Factor認(rèn)證; 快速實...
摘要:推薦序前言致謝第一章引言第二章創(chuàng)建和銷毀對象第項用靜態(tài)工廠方法代替構(gòu)造器第項遇到多個構(gòu)造器參數(shù)時要考慮使用構(gòu)建器第項用私有構(gòu)造器或者枚舉類型強化屬性第項通過私有構(gòu)造器強化不可實例化的能力第項優(yōu)先考慮依賴注入來引用資源第項避免創(chuàng)建不必要的對象 推薦序 前言 致謝 第一章 引言 第二章 創(chuàng)建和銷毀對象 第1項:用靜態(tài)工廠方法代替構(gòu)造器 第2項:遇到多個構(gòu)造器參數(shù)時要考慮使用構(gòu)建器 第...
摘要:本章中的大部分內(nèi)容適用于構(gòu)造函數(shù)和方法。第項其他方法優(yōu)先于序列化第項謹(jǐn)慎地實現(xiàn)接口第項考慮使用自定義的序列化形式第項保護性地編寫方法第項對于實例控制,枚舉類型優(yōu)先于第項考慮用序列化代理代替序列化實例附錄與第版中項目的對應(yīng)關(guān)系參考文獻 effective-java-third-edition 介紹 Effective Java 第三版全文翻譯,純屬個人業(yè)余翻譯,不合理的地方,望指正,感激...
摘要:利用前面所述的方法,這個例子可以用方法引用改寫成下面的樣子構(gòu)造函數(shù)引用對于一個現(xiàn)有構(gòu)造函數(shù),你可以利用它的名稱和關(guān)鍵字來創(chuàng)建它的一個引用。 第三章 Lambda表達(dá)式 函數(shù)式接口 函數(shù)式接口就是只定義一個抽象方法的接口,哪怕有很多默認(rèn)方法,只要接口只定義了一個抽象方法,它就仍然是一個函數(shù)式接口。 常用函數(shù)式接口 showImg(https://segmentfault.com/img...
閱讀 993·2021-10-25 09:48
閱讀 697·2021-08-23 09:45
閱讀 2550·2019-08-30 15:53
閱讀 1805·2019-08-30 12:45
閱讀 734·2019-08-29 17:21
閱讀 3548·2019-08-27 10:56
閱讀 2605·2019-08-26 13:48
閱讀 755·2019-08-26 12:24