Java8 Optional 的正確使用方式

https://www.jianshu.com/p/c169ddd34903

https://www.jdon.com/52008

1.當我們還在以如下幾種方式使用 Optional 時, 就得開始檢視自己了

  • 調用 isPresent() 方法時
  • 調用 get() 方法時
  • Optional 類型作爲類/實例屬性時
  • Optional 類型作爲方法參數時
  1. isPresent() 與 obj != null 無任何區別, 我們的生活依然在步步驚心. 而沒有 isPresent() 作鋪墊的 get() 調用在 IntelliJ IDEA 中會收到告警。調用 Optional.get() 前不事先用 isPresent() 檢查值是否可用. 假如 Optional 不包含一個值, get() 將會拋出一個異常!
  2. 把 Optional 類型用作屬性或是方法參數在 IntelliJ IDEA 中更是強力不推薦的!
  3. 使用任何像 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()

  1. Optional.of(obj): 它要求傳入的 obj 不能是 null 值的, 否則還沒開始進入角色就倒在了 NullPointerException 異常上了.
  2. 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://www.jianshu.com/p/c169ddd34903
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章