# Optional類別 ## Optional 是值的容器 Optional只有兩種狀態,不是有值就是沒值。目的是做為 null 的替代方案。Optional 提供工廠方法,將你輸入的值產生為 Optional 物件,這時Optional 物件即為該值的容器,若要取回該值,必須使用 get() 方法。 Optional 屬於 value-based,對於識別敏感的操作(包含參考相等的判斷(==)、hash code或同步(synchronization)等)會有不確定性的結果,應該要避免使用這些操作。 ## 將值轉為 Optional 的方法 * of():接受非 null 的值並回傳 Optional 物件。 * ofNullable():可以接受 null 的值,回傳 Optional 物件。 ## 取得放在 Optional 物件內的值的方法 * get():如果值存在就回傳這個值,否則就丟出 NoSuchElementException。 * orElse(T other):如果值存在就回傳這個值,否則回傳 other 。 * orElseGet(Supplier< ? extends T > other):如果值存在就回傳這個值,否則就呼叫 other 並回傳它的結果。 * orElseThrow(Supplier< ? extends X > exceptionSupplier):如果值存在就回傳這個值,否則就丟出由 exceptionSupplier 建立的例外。 ## 檢查值是否存在 * boolean isPresent():如果值存在,回傳 true;不存在則回傳 false。 * void ifPresent(Consumer< ? super T > consumer):如果值存在,呼叫指定的 consumer 物件並將值傳給它處理;不存在則什麼都不做。 ## 從範例學怎麼用 Optional ### 範例1:值不是null的情況 ``` private void nameNotNull() { String name = "Tony"; Optional<String> optName = Optional.of(name); System.out.println(optName.get()); } ``` 原本的字串 name 透過 Optional.of() 方法包裝成 Optional 物件,然後用 get() 方法取出,在 name 不是 null 的情況,一切正常。 值不是 null 的情況沒什麼好處理的,因此之後的情況都會著重在怎麼處理 null 發生時。 ### 範例2:同範例1但值變成 null 了 ``` private void nameNull() { String name = null; Optional<String> optName = Optional.of(name);//of method make NPE Boom System.out.println(optName.get()); } ``` 因為 of() 方法不接受 null,所以在產生 Optional 物件時,就發生錯誤了,而且是大魔王 NullPointerException。Optional 不是設計用來處理 null 的嗎?怎麼不行。這裡要改用另一個 ofNullable() 方法,繼續看下去。 ### 範例3:改用ofNullable() ``` private void nameNullable() { String name = null; Optional<String> optName = Optional.ofNullable(name); System.out.println(optName.get());//get method make NoSuchElementException: No value present } ``` 可以了,null 值一樣可以產生 Optional 物件。BUT!在取值時出現例外,好險不是大魔王而是 NoSuchElementException。ofNullable 雖然可以產生 Optional 物件,可是因為原本的值是 null,所以雖然包裝成 Optional 依然是沒有值的狀態,對它取值當然就出現找不到的情況。 那這樣到底要 Optional 幹嘛? ### 範例4:使用前先檢查 ``` private void nameIsPresent() { String name = null; Optional<String> optName = Optional.ofNullable(name); if (optName.isPresent()) { System.out.println(optName.get()); } else { System.out.println("Name is null."); } } ``` 用 of() 的情況是絕對不能有 null,ofNullable() 則允許 null,它們最終都是將值包裝成 Optional 物件。我們可以使用 isPresent() 方法來確認是不是有值,這裡的動作和我們常寫的 if(name != null) 其實作用是相同的。檢查後再取值就萬無一失了,不過看起來好像換湯不換藥。 !重要守則:在每次呼叫 get() 前,先呼叫 isPresent() 確保,或改用 orElse()。 ### 範例5:包裝值的另一個方式 ``` private void nameEmpty() { String name = null; Optional<String> optName = (name == null) ? Optional.empty() : Optional.of(name); System.out.println(optName.get());//get method make NoSuchElementException: No value present } ``` 我們可以使用三元運算子來判斷,如果是 null 就回傳 Optional.empty(),有值就用 of()。這樣可以避掉丟 null 給 of() 的風險,同時也回傳 Optional 提供的 empty(),表示為無值。因為是無值,所以用 get() 一樣會丟出例外,還是要用 isPresent() 確認。 ### 範例6:使用 orElse 就可以丟掉 if 了 ``` private void nameOrElse() { String name = null; Optional<String> optName = Optional.ofNullable(name); System.out.println(optName.orElse("Name is null.")); } ``` orElse() 會做兩件事,先用 isPresent() 確認有無值,有值就直接 get() 返回值;否則就把參數中的值回傳。這樣就可以把原本 5 行的 if 判斷句縮減成一行。 ### 範例7:使用 orElseGet ``` private void nameOrElseGet() { String name = null; Optional<String> optName = Optional.ofNullable(name); System.out.println(optName.orElseGet(() -> "WHAT! null!"));//lambda } ``` 如果你要在無值時多做一些事,可以改用 orElseGet 來做,它的參數是 Supplier 介面,這裡使用 lambda expression 來簡化程式碼。 ### 範例8:使用 OrElseThrow 丟出例外 ``` private void nameOrElseThrow() { class MyException extends Exception { public MyException(String message) { super(message); } } String name = null; Optional<String> optName = Optional.ofNullable(name); try { System.out.println(optName.orElseThrow(() -> new MyException("WHAT! NULL!"))); } catch (MyException e){ System.out.println("MY EXCEPTION! " + e.getMessage()); } } ``` 如果你想在無值時丟出例外,就使用 OrElseThrow。 ###### tags: `java` `optional`