巧用Optional擺脫NullPointExcept的折磨
背景
在Java中,如果你嘗試對null做函數(shù)調(diào)用,就會引發(fā)NullPointerException(NPE),NPE是Java程序開發(fā)中的典型的異常,對于Java開發(fā)者來說,無論你是初出茅廬的新人和還工作多年的老司機,NPE經(jīng)常讓他們翻車。為了避免NPE,他們會加很多if判斷語句,使得代碼的可讀性變得很差。
從軟件設計的角度來看,null本身是沒有意義的語義,這是一種對缺失變量值的錯誤的建模。
從Java類型系統(tǒng)的角度看,null可以被賦值給任何類型的變量,并且不斷被傳遞,知道***誰也不知道它是從哪里引入的。
Optional的引入
Java設計者從Haskell和Scala中獲取靈感,在Java 8中引入了一個新的類 java.util.Optional<T>。如果一個接口返回Optional,可以表示一個人可能有車也可能沒有車,這個比簡單的返回Car要更明確,閱讀代碼的人不需要提前準備業(yè)務知識。
Optional的目的就在于此:通過類型系統(tǒng)讓你的領域模型中隱藏的知識顯式地體現(xiàn)在你的代碼中。
Optional的使用

上面這張表里列舉了Optional的基礎API,我這里列舉了一些使用的tips:
- 你可以用ofNullable將一個可能為null的對象封裝為Optional對象,然后獲取值的時候使用orElse方法提供默認值;可以使用empty方法創(chuàng)建一個空的Optional對象;of方法一般不用,不過如果你知道某個值不可能為null,則可以用Optional封裝該值,這樣它一旦為null就會拋出異常。

- 從某個對象中獲取值是最常見的一種場景,這時候為了避免這個對象為null導致NPE,一般是使用if-then-else結(jié)構(gòu)檢查,如果使用Optional的話,則可以使用map方法來獲取它封裝的對象中某個字段的值。

- 如果需要連續(xù)、層層遞進的從某個對象鏈的末端獲取字段的值,則不能全部使用map方法,需要先使用flatMap,***再使用map方法;Optional中的map、flatMap和filter方法,在概念是與Stream中對應的方法都很類似,區(qū)別就在于Optional中的元素至多有一個,算是Stream的一種特殊情況——一種特殊的集合。

- 不要使用ifPresent和get方法,它們本質(zhì)上和不適用Optional對象之前的模式相同,都是臃腫的if-then-else判斷語句;
- 由于Optional無法序列化,所以在領域模型中,無法將某個字段定義為Optional的,原因是:Optional的設計初衷僅僅是要支持能返回Optional對象的語法,如果我們希望在域模型中引入Optional,則可以用下面這種替代的方法:

- 不要使用基礎類型的Optional對象,原因是:基礎類型的Optional對象不支持map、flatMap和filter方法,而這些方法是Optional中非常強大的方法。
實戰(zhàn)案例
使用工具類方法改良可能拋出異常的API
Java方法處理異常結(jié)果的方式有兩種:返回null(或錯誤碼);拋出異常,例如:Integer.parseInt(String)這個方法——如果無法解析到對應的整型,該方法就拋出一個NumberFormationException,這種情況下我們一般會使用try/catch語句處理異常情況。
一般我們建議將try/catch塊單獨提取到一個方法中,在這里使用Optional設計這個方法,代碼如下。在開發(fā)中,可以嘗試構(gòu)建一個OptionalUtility工具類,將這些復雜的try/catch邏輯封裝起來。

綜合案例
現(xiàn)在有個方法,是嘗試從一個屬性映射中獲取某個關(guān)鍵詞對應的值,例子代碼如下:

使用Optional的寫法后,代碼如下所示:

如果需要訪問的屬性值不存在,Properites.getProperty(String)方法的返回值就是一個null,使用noNullable工廠方法就可以將該值轉(zhuǎn)換為Optional對象;接下來,可以使用flatMap將一個Optional<String>轉(zhuǎn)換為Optional<Integer>對象;***使用filter過濾掉負數(shù),然后就可以使用orElse獲取屬性值,如果拿不到則返回默認值0。
總結(jié)
使用Optional的思路和Stream相同,都是鏈式思路,跟數(shù)據(jù)庫查詢似的,表達力很強,而且省去了哪些復雜的try/catch和if-then-else方法。在后面的開發(fā)中,可以使用Optional設計API,這樣可以設計出更安全的接口和方法。