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