簡介
Optional類是Java 8新增的一個類,Optional 類主要解決的問題是臭名昭著的空指標異常(NullPointerException)。 —— 每個 Java 程式設計師都非常瞭解的異常。
本篇文章將詳細介紹optional類,以及如何用它消除程式碼中的null
檢查。
本質上,這是一個包含有可選值的包裝類,這意味著 Optional 類既可以含有物件也可以為空。
Optional 是 Java 實現函數語言程式設計的強勁一步,並且幫助在正規化中實現。
但是,Optional 的意義顯然不止於此。
避免使用null
檢查
作為Java開發人員,幾乎所有人都遇到過NullPointerException
異常,大多數人遇到NullPointerException
異常時都會在異常出現的地方加上if
程式碼塊來判斷值不為空,比如下面的程式碼:
public void bindUserToRole(User user) { if (user != null) { String roleId = user.getRoleId(); if (roleId != null) { Role role = roleDao.findOne(roleId); if (role != null) { role.setUserId(user.getUserId()); roleDao.save(role); } } } }
這是比較普遍的做法,為了避免出現NullPointerException
異常,手動對可能為null
值進行了處理,不過程式碼看起來非常糟糕,業務邏輯被淹沒在if
邏輯判斷中,也許下面的程式碼看起來可讀性稍好一些:
public String bindUserToRole(User user) { if (user == null) { return; } String roleId = user.getRoleId(); if (roleId == null) { return; } Role = roleDao.findOne(roleId); if (role != null) { role.setUserId(user.getUserId()); roleDao.save(role); } }
上面的程式碼避免了深層的if
語句巢狀,但本質上是一樣的,方法內有三個不同的返回點,出錯後除錯也不容易,因為你不知道是那個值導致了NullPointerException
異常。
基於上面的原因,Java 8中引入了一個新的類Optional
,用以避免使用null
值引發的種種問題。
Optional
類
java.util.Optional<T>
類是一個封裝了Optional
值的容器物件,Optional
值可以為null
,如果值存在,呼叫isPresent()
方法返回true
,呼叫get()
方法可以獲取值。
建立Optional
物件
1、Optional
類提供了三個方法用於例項化一個Optional
物件,它們分別為empty()
、of()
、ofNullable()
,這三個方法都是靜態方法,可以直接呼叫。
empty()
方法用於建立一個沒有值的Optional
物件:
Optional<String> emptyOpt = Optional.empty();
empty()
方法建立的物件沒有值,如果對emptyOpt
變數呼叫isPresent()
方法會返回false
,呼叫get()
方法丟擲NullPointerException
異常。
2、of()
方法使用一個非空的值建立Optional
物件:
String str = "Hello World";
Optional<String> notNullOpt = Optional.of(str);
3、ofNullable()
方法接收一個可以為null
的值:
Optional<String> nullableOpt = Optional.ofNullable(str);
如果str
的值為null
,得到的nullableOpt
是一個沒有值的Optional
物件。
提取Optional
物件中的值
如果我們要獲取User
物件中的roleId
屬性值,常見的方式是直接獲取:
String roleId = null; if (user != null) { roleId = user.getRoleId(); }
使用Optional
中提供的map()
方法可以以更簡單的方式實現:
Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::getRoleId);
使用orElse()
方法獲取值
Optional
類還包含其他方法用於獲取值,這些方法分別為:
orElse()
:如果有值就返回,否則返回一個給定的值作為預設值;orElseGet()
:與orElse()
方法作用類似,區別在於生成預設值的方式不同。該方法接受一個Supplier<? extends T>
函式式介面引數,用於生成預設值;orElseThrow()
:與前面介紹的get()
方法類似,當值為null
時呼叫這兩個方法都會丟擲NullPointerException
異常,區別在於該方法可以指定丟擲的異常型別。
下面來看看這三個方法的具體用法:
String str = "Hello World"; Optional<String> strOpt = Optional.of(str); String orElseResult = strOpt.orElse("Hello Shanghai"); String orElseGet = strOpt.orElseGet(() -> "Hello Shanghai"); String orElseThrow = strOpt.orElseThrow( () -> new IllegalArgumentException("Argument 'str' cannot be null or blank."));
此外,Optional
類還提供了一個ifPresent()
方法,該方法接收一個Consumer<? super T>
函式式介面,一般用於將資訊列印到控制檯:
Optional<String> strOpt = Optional.of("Hello World");
strOpt.ifPresent(System.out::println);
使用filter()
方法過濾
filter()
方法可用於判斷Optional
物件是否滿足給定條件,一般用於條件過濾:
Optional<String> optional = Optional.of("lw900925@163.com");
optional = optional.filter(str -> str.contains("164"));
在上面的程式碼中,如果filter()
方法中的Lambda表示式成立,filter()
方法會返回當前Optional
物件值,否則,返回一個值為空的Optional
物件。
如何正確使用Optional
通過上面的例子可以看出,Optional
類可以優雅的避免NullPointerException
帶來的各種問題,不過,你是否真正掌握了Optional
的用法?假設你試圖使用Optional
來避免可能出現的NullPointerException
異常,編寫了如下程式碼:
Optional<User> userOpt = Optional.ofNullable(user); if (userOpt.isPresent()) { User user = userOpt.get(); // do something... } else { // do something... }
坦白說,上面的程式碼與我們之前的使用if
語句判斷空值沒有任何區別,沒有起到Optional
的真正作用:
if (user != null) { // do something... } else { // do something... }
當我們從之前版本切換到Java 8的時候,不應該還按照之前的思維方式處理null
值,Java 8提倡函數語言程式設計,新增的許多API都可以用函數語言程式設計表示,Optional
類也是其中之一。
這裡有幾條關於Optional
使用的建議:
- 儘量避免在程式中直接呼叫
Optional
物件的get()
和isPresent()
方法; - 避免使用
Optional
型別宣告實體類的屬性;
第一條建議中直接呼叫get()
方法是很危險的做法,如果Optional
的值為空,那麼毫無疑問會丟擲NullPointerException
異常,而為了呼叫get()
方法而使用isPresent()
方法作為空值檢查,這種做法與傳統的用if
語句塊做空值檢查沒有任何區別。
第二條建議避免使用Optional
作為實體類的屬性,它在設計的時候就沒有考慮過用來作為類的屬性,如果你檢視Optional
的原始碼,你會發現它沒有實現java.io.Serializable
介面,這在某些情況下是很重要的(比如你的專案中使用了某些序列化框架),使用了Optional
作為實體類的屬性,意味著他們不能被序列化。
正確建立Optional
物件
上面提到建立Optional
物件有三個方法,empty()
方法比較簡單,沒什麼特別要說明的。
主要是of()
和ofNullable()
方法。當你很確定一個物件不可能為null
的時候,應該使用of()
方法,否則,儘可能使用ofNullable()
方法,比如:
public static void method(Role role) { // 當Optional的值通過常量獲得或者通過關鍵字new初始化,可以直接使用of()方法 Optional<String> strOpt = Optional.of("Hello World"); Optional<User> userOpt = Optional.of(new User()); // 方法引數中role值不確定是否為null,使用ofNullable()方法建立 Optional<Role> roleOpt = Optional.ofNullable(role); }
orElse()
方法的使用
return str != null ? str : "Hello World"
上面的程式碼表示判斷字串str
是否為空,不為空就返回,否則,返回一個常量。使用Optional
類可以表示為:
return strOpt.orElse("Hello World")
簡化if-else
User user = ... if (user != null) { String userName = user.getUserName(); if (userName != null) { return userName.toUpperCase(); } else { return null; } } else { return null; }
上面的程式碼可以簡化成:
User user = ... Optional<User> userOpt = Optional.ofNullable(user); return userOpt.map(User::getUserName).map(String::toUpperCase).orElse(null);
總結一下,新的Optional
類讓我們可以以函數語言程式設計的方式處理null
值,拋棄了Java 8之前需要巢狀大量if-else
程式碼塊,使程式碼可讀性有了很大的提高。
本文參考連結:部落格【一書生VOID】https://lw900925.github.io/java/java8-optional.html