你用過 Java Optional 嗎?
本文假設讀者你已經用過 Optional 或是介接過回傳 Optional 的 API,知道 Optional 主要是解決 java.lang.NullPointerException(剛好是本站用的網址 XD),但還是不太知道為什麼要用 Optional!
換個角度看 Java Optional
我覺得站在開發 API 的角度來看這個問題,就很清楚為何要使用 Optional!
- 如果回傳的結果可能為 null 就該用 Optional<T>。
- 反之,不可能回傳 null 就跟以前一樣就用 T。
使用你 API 的人,就可以根據這個資訊,知道呼叫這個方法需不需要處理 null 的問題,而不是每個方法都要多寫處理 null 的邏輯,或是沒處裡就會發生 java.lang.NullPointerException。
// 回傳 People 代表這方法絕對會回傳一個我爸
public People getMyFather()
// 回傳 <Optional>People 代表可能回傳空物件
public <Optional>People getMyFather()
如果今天你寫的系統,每個人都必定會有個爸爸,那回傳 People 的 getMyFather() 符合你的意圖,而回傳 <Optional>People 的 getMyFather() 因為有可能回傳空物件的語意,所以就不適合用此方法。
若你寫的系統允許父不詳的狀況,那回傳 <Optional>People 的 getMyFather() 就是比較好的寫法。
我看到第一個回傳 People 的 getMyFather(),我很清楚我不用處理 null 的狀況,也就不用多寫處理 null 的邏輯。而第二個方法我就需要用 isPresent() 檢查回傳的 Optional<People>是否為一個空物件。
Java Optional 常用的方法整理
下面程式碼說明 of、ofNullable 這兩個方法:
Foo foo = null;
// of()不接受傳入 null,此處會發生 NullPointerException
Optional<Foo> optionalFoo1 = Optional.of(foo);
// ofNullable()可以傳入 null,此處會得到一個空物件
Optional<Foo> optionalFoo2 = Optional.ofNullable(foo);
下面程式碼說明 ifPresent、get、getElse、orElseGet、orElseThrow:
// 從 getBar 取得一個 Optional<Bar>
// 由回傳 Optional<Bar> 來看
// 我們預期有可能收到空物件
Optional<Bar> optionalBar = getBar();
// 用 ifPresent + get 的寫法跟原本檢查 null 的寫法一樣差
if (optionalBar.ifPresent()) // 檢查是否為空物件
{
Bar bar = optionalBar.get(); // 取得包在裏頭的 bar
bar.doSometing();
}
// 當 optionalBar 裏頭包的是 null,會建立一個新的 Bar 物件並回傳
Bar bar = optionalBar.getElse(new Bar());
// 當 optionalBar 裏頭包的是 null,會建立一個新的 Bar 物件並回傳
Bar bar = optionalBar.orElseGet(() -> {
// 這邊可以多做一些事,比如印 Log
return new Bar();
}));
// 當 optionalBar 裏頭包的是 null,會丟出一個自訂的例外
Bar bar = optionalBar.orElseThrow(() -> new MyException("WTF")));
結語
開發 API 的人使用 Optional 來說明回傳的物件是不是有可能為 null,讓使用 API 的人看到介面就知道是否要處理 null。
而 getElse() 能改善以往檢查是否為空,如果不為空就...的寫法,讓程式碼更簡潔更容易閱讀。