Optional 類解決空指針異常
前言
空指針異常是導(dǎo)致 Java 應(yīng)用程序失敗的最常見原因。以前,為了解決空指針異常,Google 公司著名的 Guava 項目引入了 Optional 類,Guava 通過使用檢查空值的方式來防止代碼污染,它鼓勵程序員寫更干凈的代碼。受到 Google Guava 的啟發(fā),Optional 類已經(jīng)成為 Java8 類庫的一部分。Optional 實際上是個容器:它可以保存類型 T 的值,或者僅僅保存 null。Optional 提供很多有用的方法,這樣我們就不用顯式進(jìn)行空值檢測。
Optional 范例
構(gòu)造方式
Optional 的三種構(gòu)造方式:Optional.of(obj),Optional.ofNullable(obj) 和 Optional.empty()。
Optional.of(),依據(jù)一個非空值創(chuàng)建 Optional
// 如果 car 是一個 null, 這段代碼會立即拋出
// NullPointException, 而不是等到試圖訪問 car 的屬性值時才返回一個錯誤.
Optional<Integer> optCar = Optional.of(car);Optional.ofNullable(),可接受 null 的 Optional(實現(xiàn)序列化的域模型時使用)
// 創(chuàng)建一個允許 null 值的 Optional 對象,
// 如果 car 是 null, 那么得到的 Optional 對象就是個空對象.
Optional<Integer> optCar = Optional.ofNullable(car);Optional.empty(),所有 null 包裝成的 Optional 對象。
// 聲明一個空的 Optional
Optional<Integer> empty = Optional.empty();
isPresent()/ifPresent(Consumer consumer)
isPresent(),判斷值是否存在。
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
// isPresent 判斷值是否存在
System.out.println(optional1.isPresent() == true);
System.out.println(optional2.isPresent() == false);
ifPresent(Consumer consumer):如果 option 對象保存的值不是 null,則調(diào)用 consumer 對象,否則不調(diào)用。
// 如果不是 null, 調(diào)用 Consumer; 如果 null, 不調(diào)用 Consumer
optional1.ifPresent(new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println("value is" + t);
}
});
// Lambda 表達(dá)式寫法
optional1.ifPresent(t -> System.out.println("value is" + t));
orElse(value)/orElseGet(Supplier supplier)
orElse(value):如果 optional 對象保存的值不是 null,則返回原來的值,否則返回 value。
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
System.out.println(optional1.orElse(1000) == 1);// true
System.out.println(optional2.orElse(1000) == 1000);// true
orElseGet(Supplier supplier):功能與 orElse 一樣,只不過 orElseGet 參數(shù)是一個對象,即無則由函數(shù)來產(chǎn)生。
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
System.out.println(optional1.orElseGet(() -> {
return 1000;
}) == 1);
// true
System.out.println(optional2.orElseGet(() -> {
return 1000;
}) == 1000);
// true
orElseThrow()
orElseThrow():值不存在則拋出異常,存在則什么不做,有點類似 Guava 的 Precoditions。
Optional<Integer> optional = Optional.ofNullable(null);
optional.orElseThrow(() -> {
throw new NullPointerException();
});
filter(Predicate)
filter(Predicate):判斷 Optional 對象中保存的值是否滿足 Predicate,并返回新的 Optional。
Optional<Integer> optional = Optional.ofNullable(1);
Optional<Integer> filter = optional.filter((a) -> a == 1);
map(Function)/flatMap(Function)
map(Function) 如果有值,則對其執(zhí)行調(diào)用 mapping 函數(shù)得到返回值,否則返回空 Optional。如果返回值不為 null,則創(chuàng)建包含 mapping 返回值的 Optional 作為 map 方法返回值,否則返回空 Optional。
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));
flatMap(Function) 如果有值,為其執(zhí)行 mapping 函數(shù)返回 Optional 類型返回值,否則返回空 Optional。flatMap 與 map(Funtion)方法類似,區(qū)別在于 flatMap 中的 mapper 返回值必須是 Optional。調(diào)用結(jié)束時,flatMap 不會對結(jié)果用 Optional 封裝。[P.S. 參考博文 [3]:Cascading Optional Objects Using the flatMap Method]
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));
Optional 類的鏈?zhǔn)椒椒?/span>
錯誤示范 / 正確示范
示范(一)
// ** 錯誤示范
Optional<User> user = ......
if (user.isPresent()) {
return user.getOrders();
} else {
return Collections.emptyList();
}
// 二者實質(zhì)上是沒有任何分別
User user = .....
if (user != null) {
return user.getOrders();
} else {
return Collections.emptyList();
}
// ** 正確示范
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
// map 是可能無限級聯(lián)的, 比如再深一層, 獲得用戶名的大寫形式
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
示范(二)
// ** 錯誤示范
String version = "UNKNOWN";
if(computer != null){
Soundcard soundcard = computer.getSoundcard();
if(soundcard != null){
USB usb = soundcard.getUSB();
if(usb != null){
version = usb.getVersion();
}
}
}
// ** 正確示范
String version = computer.map(Computer::getSoundcard)
.map(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");參考博文
[1]. Java 8 Optional 類深度解析
[2]. 使用 Java8 Optional 的正確姿勢
[3]. Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!
source:https://morning-pro.github.io/archives/8eb6feba.html
喜歡,在看
