Optional 類解決空指針異常

前言

空指針異常是導致 Java 應用程序失敗的最常見原因。以前,爲了解決空指針異常,Google 公司著名的 Guava 項目引入了 Optional 類,Guava 通過使用檢查空值的方式來防止代碼污染,它鼓勵程序員寫更乾淨的代碼。受到 Google Guava 的啓發,Optional 類已經成爲 Java8 類庫的一部分。Optional 實際上是個容器:它可以保存類型 T 的值,或者僅僅保存 null。Optional 提供很多有用的方法,這樣我們就不用顯式進行空值檢測。

Optional 範例

構造方式

Optional 的三種構造方式:Optional.of(obj),Optional.ofNullable(obj) 和 Optional.empty()。

// 如果 car 是一個 null, 這段代碼會立即拋出 
// NullPointException, 而不是等到試圖訪問 car 的屬性值時才返回一個錯誤.
Optional<Integer> optCar = Optional.of(car);
// 創建一個允許 null 值的 Optional 對象, 
// 如果 car 是 null, 那麼得到的 Optional 對象就是個空對象.
Optional<Integer> optCar = Optional.ofNullable(car);
// 聲明一個空的 Optional
Optional<Integer> empty = Optional.empty();

isPresent()/ifPresent(Consumer consumer)

isPresent(),判斷值是否存在。

Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

// isPresent 判斷值是否存在
System.out.println(optional1.isPresent() == true);
System.out.println(optional2.isPresent() == false);

ifPresent(Consumer consumer):如果 option 對象保存的值不是 null,則調用 consumer 對象,否則不調用。

// 如果不是 null, 調用 Consumer; 如果 null, 不調用 Consumer
optional1.ifPresent(new Consumer<Integer>() {
    @Override
    public void accept(Integer t) {
        System.out.println("value is" + t);
    }
});

// Lambda 表達式寫法
optional1.ifPresent(t -> System.out.println("value is" + t));

orElse(value)/orElseGet(Supplier supplier)

orElse(value):如果 optional 對象保存的值不是 null,則返回原來的值,否則返回 value。

Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

System.out.println(optional1.orElse(1000) == 1);// true
System.out.println(optional2.orElse(1000) == 1000);// true

orElseGet(Supplier supplier):功能與 orElse 一樣,只不過 orElseGet 參數是一個對象,即無則由函數來產生。

Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

System.out.println(optional1.orElseGet(() -> {
    return 1000;
}) == 1);
// true

System.out.println(optional2.orElseGet(() -> {
    return 1000;
}) == 1000);
// true

orElseThrow()

orElseThrow():值不存在則拋出異常,存在則什麼不做,有點類似 Guava 的 Precoditions。

Optional<Integer> optional = Optional.ofNullable(null);

optional.orElseThrow(() -> {
    throw new NullPointerException();
});

filter(Predicate)

filter(Predicate):判斷 Optional 對象中保存的值是否滿足 Predicate,並返回新的 Optional。

Optional<Integer> optional = Optional.ofNullable(1);

Optional<Integer> filter = optional.filter((a) -> a == 1);

map(Function)/flatMap(Function)

map(Function) 如果有值,則對其執行調用 mapping 函數得到返回值,否則返回空 Optional。如果返回值不爲 null,則創建包含 mapping 返回值的 Optional 作爲 map 方法返回值,否則返回空 Optional。

Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));

flatMap(Function) 如果有值,爲其執行 mapping 函數返回 Optional 類型返回值,否則返回空 Optional。flatMap 與 map(Funtion)方法類似,區別在於 flatMap 中的 mapper 返回值必須是 Optional。調用結束時,flatMap 不會對結果用 Optional 封裝。[P.S. 參考博文 [3]:Cascading Optional Objects Using the flatMap Method]

upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));

Optional 類的鏈式方法

錯誤示範 / 正確示範

// ** 錯誤示範
String version = "UNKNOWN";
if(computer != null){
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null){
    USB usb = soundcard.getUSB();
    if(usb != null){
      version = usb.getVersion();
    }
  }
}

// ** 正確示範
String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");

參考博文

[1]. Java 8 Optional 類深度解析
[2]. 使用 Java8 Optional 的正確姿勢
[3]. Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!

source:https://morning-pro.github.io/archives/8eb6feba.html
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/mbtvXxcCl1sTpyRvjPvqUA