---
# System prepended metadata

title: Optional類別
tags: [optional, Java]

---


# 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`