Java8 Optional 的正確使用方式
1.當我們還在以如下幾種方式使用 Optional 時, 就得開始檢視自己了
- 呼叫 isPresent() 方法時
- 呼叫 get() 方法時
- Optional 型別作為類/例項屬性時
- Optional 型別作為方法引數時
- isPresent() 與 obj != null 無任何區別, 我們的生活依然在步步驚心. 而沒有 isPresent() 作鋪墊的 get() 呼叫在 IntelliJ IDEA 中會收到告警。呼叫 Optional.get() 前不事先用 isPresent() 檢查值是否可用. 假如 Optional 不包含一個值, get() 將會丟擲一個異常!
- 把 Optional 型別用作屬性或是方法引數在 IntelliJ IDEA 中更是強力不推薦的!
- 使用任何像 Optional 的型別作為欄位或方法引數都是不可取的. Optional 只設計為類庫方法的, 可明確表示可能無值情況下的返回型別. Optional 型別不可被序列化, 用作欄位型別會出問題的!!!
所以 Optional 中我們真正可依賴的應該是除了 isPresent() 和 get() 的其他方法:
//按照使用頻率排序如下 public<U> Optional<U> map(Function<? super T, ? extends U> mapper) public T orElse(T other) public T orElseGet(Supplier<? extends T> other) public void ifPresent(Consumer<? super T> consumer) public Optional<T> filter(Predicate<? super T> predicate) public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
Optional 的三種構造方式:
Optional.of(obj), Optional.ofNullable(obj) 和明確的 Optional.empty()
- Optional.of(obj): 它要求傳入的 obj 不能是 null 值的, 否則還沒開始進入角色就倒在了 NullPointerException 異常上了.
- Optional.ofNullable(obj): 它以一種智慧的, 寬容的方式來構造一個 Optional 例項. 來者不拒, 傳 null 進到就得到 Optional.empty(), 非 null 就呼叫 Optional.of(obj).
那是不是我們只要用 Optional.ofNullable(obj) 一勞永逸, 以不變應二變的方式來構造 Optional 例項就行了呢? 那也未必, 否則 Optional.of(obj) 何必如此暴露呢, 私有則可?
使用Optional.of(obj)原則
當我們非常非常的明確將要傳給 Optional.of(obj) 的 obj 引數不可能為 null 時, 比如它是一個剛 new 出來的物件(Optional.of(new User(...))), 或者是一個非 null 常量時; 2. 當想為 obj 斷言不為 null 時, 即我們想在萬一 obj 為 null 立即報告 NullPointException 異常, 立即修改, 而不是隱藏空指標異常時, 我們就應該果斷的用 Optional.of(obj) 來構造 Optional 例項, 而不讓任何不可預計的 null 值有可乘之機隱身於 Optional 中.
以下為Optional<T>的正確使用方式:
- 存在即返回, 無則提供預設值
return user.orElse(null); //而不是 return user.isPresent() ? user.get() : null;
return user.orElse(UNKNOWN_USER);
- 存在即返回, 無則由函式來產生
return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();
- 存在才對它做點什麼
user.ifPresent(System.out::println);
//而不要下邊那樣
if (user.isPresent()) {
System.out.println(user.get());
}
map 函式隆重登場
當 user.isPresent() 為真, 獲得它關聯的 orders的對映集合, 為假則返回一個空集合時, 我們用上面的 orElse, orElseGet 方法都乏力時, 那原本就是 map 函式的責任, 我們可以這樣一行
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
//上面避免了我們類似 Java 8 之前的做法
if(user.isPresent()) {
return user.get().getOrders();
} else {
return Collections.emptyList();
}
map 是可能無限級聯的, 比如再深一層, 獲得使用者名稱的大寫形式
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
這要擱在以前, 每一級呼叫的展開都需要放一個 null 值的判斷
User user = .....
if(user != null) {
String name = user.getUsername();
if(name != null) {
return name.toUpperCase();
} else {
return null;
}
} else {
return null;
}
- filter() :如果有值並且滿足條件返回包含該值的Optional,否則返回空Optional。
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//輸出Sanaulla
- flatMap() :
如果有值,為其執行mapping函式返回Optional型別返回值,否則返回空Optional。flatMap與map(Funtion)方法類似,區別在於flatMap中的mapper返回值必須是Optional。呼叫結束時,flatMap不會對結果用Optional封裝。
flatMap方法與map方法類似,區別在於mapping函式的返回值不同。map方法的mapping函式返回值可以是任何型別T,而flatMap方法的mapping函式必須是Optional。
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//輸出SANAULLA
- orElseThrow() 在有值時直接返回, 無值時丟擲想要的異常.
我的部落格即將搬運同步至騰訊雲+社群,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1x15vtpzcl599
------------------------------------------------------------------------------------------------------
作者:神祕的寇先森
連結:https://www.jianshu.com/p/c169ddd34903
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。