9. Optional

9.1 什麼是Optional

寫程式時常常會意外遇到NullPointerException的情形,常發生在開發者沒注意或不清楚函式是否會回傳null而沒有檢查,或者撰寫函式時,由於沒有資料或不知道要回傳什麼就直覺回傳null,因此就常常發生此種情況。

Optional就是用來改進這種常遇到null而發生錯誤的情形而產生的。一個Optional物件,代表可能他含有T物件,也可能沒有(null),因此看到Optional就知道要記得處理發生null的情況。

9.2 取得Optional內的值、或產生替代值

譬如,以ID來取得一個員工的姓名,用傳統的作法來檢查是否為null:


String name = getEmployName("S1233"); //回傳String或null

if(name != null){ //檢查是否為null
 return name;
}else{
 return "not exist";
}

如果將getEmployName()的回傳值用Optional包起來(也就是回傳的是Optional<String>),就會像這樣:


String nameOptional = getEmploynName("S1233"); //回傳Optional<String>物件

if(nameOptional.isPresent()){ //檢查Optional中是否有包含物件
 return nameOptional.get();
}else{
 return "not exist";
}

不過這樣看起來似乎沒有比較簡單?使用Optional的重點在於:要嘛取出所包含的值,要嘛就是產生替代值。 我們將上面的程式碼再改一次:


String nameOptional = getEmploynName("S1233"); //回傳Optionall<Employ>物件或null

//丟出替代字串
return nameOptional.orElse("not exist");

//或丟出函式回傳的值
return nameOptional.orElseGet(() -> this.getWrongMessage());

//或丟出異常
return nameOptional.orElseThrow(NoSuchElementException::new);

9.3 產生Optional

如果要將一個值用Optional包裝起來,可以用Optional.of()或Optional.ofNullable()方法:


Optional.of(data); //確定data不是null

Optional.ofNullable(data); //不確定data會不會是null,一種過渡方法

Optional.ofNullable(null); //產生一個包含null的Optional

Optional.empty(); //同上

9.4 處理Optional內的值

如果Optional中有包含值,還可以再針對元素做處理:


Optional.ofNullable(name).map(String::length); //回傳Optional<Integer>

Optional.ofNullable(name).ifPresent(x -> list.add(x)); //不回傳

看到map()方法,是不是覺得跟Stream很像呢?其實Optional可以把它想像成是只包含一個元素的Stream,這樣就很好理解了。因此Optional也有flatMap()方法:


Optional<Double> result = Optional.of(4.0).flatMap(Test::squareRoot).orElse(0.0);
...
public static Optional<Double> squareRoot(double x) {
    return (x < 0) ? Optional.empty() : Optional.of(Math.sqrt(x));
}