使用Optional取代null

使用Optional取代null

JDK8新增了java.util.Optional類別,由於null的根本問題在於含糊而不明確,要避免使用null的方式,
就是確認過去使用null的時機與目的,並使用明確的語義。在過去使用null的情況中,開發者於方法中傳回null,
通常代表著客戶端必須檢查是否為null,並在null的情況下使用預設值,以便後續程式繼續執行。舉個例子來說:

Example

在下方這段程式碼中,如果呼叫getNickName()時忘了檢查null,那麼就會直接顯示null,
在這個簡單的例子中並不會怎樣,只是顯示結果令人困惑罷了,但如果後續的執行流程牽涉到至關重要的結果,
程式繼續執行下去,錯誤可能到最後才會呈現發生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
String nickName = getNickName("Duke");
if (nickName == null) {
nickName = "Openhome Reader";
}
out.println(nickName);
}

static String getNickName(String name) {
Map<String, String> nickNames = new HashMap<>();
nickNames.put("Justin", "caterpillar");
nickNames.put("Monica", "momor");
nickNames.put("Irene", "hamimi");
return nickNames.get(name); // key不存在時會傳回null
}

To revise example code 1

可將getNickName修改使一定會傳回Optional實例,但絕不要傳回null。
Optional的語義是它可能包含也可能不包括值,要建立Optional實例有幾個靜態方式,使用of()方法可以指定非null值建立Optional實例,
使用empty()方法可以建立不包裏值的Optional實例。
例如,可使用Optional來改寫上頭的getNickName()方法:

1
2
3
4
5
6
7
8
static Optional<String> getNickName(String name) {
Map<String, String> nickNames = new HashMap<>();
nickNames.put("Justin", "caterpillar");
nickNames.put("Monica", "momor");
nickNames.put("Irene", "hamimi");
String nickName = nickNames.get(name);
return nickName == null ? Optional.empty() : Optional.of(nickName);
}

To revise example code 2

在Optional沒有包含值的情況下,就會直接拋出java.util.NoSuchElementException,
這實現了速錯(Fail fast)的概念,這讓開發者可以立即發現錯誤,並瞭解到必須使用程式碼作些檢查,
可能的方式之一像是:

1
2
3
4
5
6
Optional<String> nickOptional = getNickName("David");
String nickName = "DC'blog Reader";
if(nickOptional.isPresent()) {
nickName = nickOptional.get();
}
out.println(nickName);

一個更好的方式可以使用orElse()方法,指定值不存在時的替代值:

1
2
Optional<String> nickOptional = getNickName("David");
out.println(nickOptional.orElse("DC'blog Reader"));

過去許多程式庫中使用了不少null,這些程式庫無法說改就改,可使用Optional的ofNullable()來銜接程式庫中會傳回null的方法,
使用ofNullable()方法時,若指定了非null值就會呼叫of()方法,指定了null值就會呼叫empty()方法。
例如,先前的getNickName()方法可以更簡潔地修改為:

1
2
3
4
5
6
7
static Optional<String> getNickName(String name) {
Map<String, String> nickNames = new HashMap<>();
nickNames.put("Justin", "caterpillar");
nickNames.put("Monica", "momor");
nickNames.put("Irene", "hamimi");
return Optional.ofNullable(nickNames.get(name));
}