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