
前言
之前遇到過使用Optional之后帶來的隱含bug,現在強調記錄一下不好的用法,防止錯用。
Optional不能序列化,不能作為類的字段(field)
這點尤為重要,即類要純粹。如果是POJO就原始類型就可以了,如果是領域對象,則更不應該使用Optional作為field。
Optional不適合作為方法參數
另一種不太適合使用Optional的情況是將該類型作為方法或者構造函數的參數,這將導致不必要的代碼復雜化。
User user = new User("john@gmail.com", "1234", Optional.empty());
而且,內部如果忘記isPresent
,會得到NoSuchElementException
。所以
相反,使用方法重載(method overloading)來處理非強制性參數要方便得多。
推薦Optional作為函數返回值
使用Optional作為返回值可以增強stream處理,構建流式API. 比如, findFirst()
就是返回一個Optional
對象。
@Test
public void whenEmptyStream_thenReturnDefaultOptional() {
List<User> users = new ArrayList<>();
User user = users.stream().findFirst().orElse(new User("default", "1234"));
assertEquals(user.getEmail(), "default");
}
Optional和steam組合更有益處
級聯調用是危險的,很容易產生空指針。比如
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
在傳統做法里,
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}
使用Optional可以精簡代碼,降低復雜度:
String result = Optional.ofNullable(user)
.flatMap(User::getAddress)
.flatMap(Address::getCountry)
.map(Country::getIsocode)
.orElse("default");
總結
Optional類對我們最有幫助的一個用例是同Stream或者其他方法組合使用,這些方法會返回一個可構建流暢API的Optional值。如果僅僅作為判空,那么不要使用Optional,直接判null就好。
比如,使用Stream 的Optional對象的例子:
@Test
public void whenGetStream_thenOk() {
User user = new User("john@gmail.com", "1234");
List<String> emails = Optional.ofNullable(user)
.stream()
.filter(u -> u.getEmail() != null && u.getEmail().contains("@"))
.map( u -> u.getEmail())
.collect(Collectors.toList());
assertTrue(emails.size() == 1);
assertEquals(emails.get(0), user.getEmail());
}
參考
原文鏈接:https://stackify.com/optional-java/
關於作者:
Eugen是一名軟件工程師,對Spring、REST API、安全和教育擁有極大熱情。同時,他還是Baeldung(推特賬號@baeldung)的創始人。