如何正確的使用Java8中的Optional類來消除代碼中的null檢查
塵埃落定!清華才子王垠加入華為職級(jí)22,前阿里P10趙海平加入字節(jié)跳動(dòng),職級(jí)或?yàn)?+ 百度網(wǎng)盤“破解版”,Pandownload開發(fā)者被抓
作者|?一書生VOID
Optional類是Java 8新增的一個(gè)類,用以解決程序中常見的NullPointerException異常問題。本篇文章將詳細(xì)介紹Optional類,以及如何用它消除代碼中的null檢查。
避免使用null檢查
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);
????????????}
????????}
????}
}
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);
????}
}
Optional類
java.util.Optional類是一個(gè)封裝了Optional值的容器對(duì)象,Optional值可以為null,如果值存在,調(diào)用isPresent()方法返回true,調(diào)用get()方法可以獲取值。
創(chuàng)建Optional對(duì)象
Optional類提供類三個(gè)方法用于實(shí)例化一個(gè)Optional對(duì)象,它們分別為empty()、of()、ofNullable(),這三個(gè)方法都是靜態(tài)方法,可以直接調(diào)用。
empty()方法用于創(chuàng)建一個(gè)沒有值的Optional對(duì)象:
Optional?emptyOpt?=?Optional.empty();
empty()方法創(chuàng)建的對(duì)象沒有值,如果對(duì)emptyOpt變量調(diào)用isPresent()方法會(huì)返回false,調(diào)用get()方法拋出NullPointerException異常。
of()方法使用一個(gè)非空的值創(chuàng)建Optional對(duì)象:
String?str?=?"Hello?World";
Optional?notNullOpt?=?Optional.of(str);
ofNullable()方法接收一個(gè)可以為null的值:
Optional?nullableOpt?=?Optional.ofNullable(str);
如果str的值為null,得到的nullableOpt是一個(gè)沒有值的Optional對(duì)象。
提取Optional對(duì)象中的值
如果我們要獲取User對(duì)象中的roleId屬性值,常見的方式是直接獲取:
String?roleId?=?null;
if?(user?!=?null)?{
????roleId?=?user.getRoleId();
}
使用Optional中提供的map()方法可以以更簡單的方式實(shí)現(xiàn):
Optional?userOpt?=?Optional.ofNullable(user);
Optional?roleIdOpt?=?userOpt.map(User::getRoleId);
使用orElse()方法獲取值
Optional類還包含其他方法用于獲取值,這些方法分別為:
orElse():如果有值就返回,否則返回一個(gè)給定的值作為默認(rèn)值; orElseGet():與orElse()方法作用類似,區(qū)別在于生成默認(rèn)值的方式不同。該方法接受一個(gè)Supplier extends T>函數(shù)式接口參數(shù),用于生成默認(rèn)值; orElseThrow():與前面介紹的get()方法類似,當(dāng)值為null時(shí)調(diào)用這兩個(gè)方法都會(huì)拋出NullPointerException異常,區(qū)別在于該方法可以指定拋出的異常類型。
下面來看看這三個(gè)方法的具體用法:
String?str?=?"Hello?World";
Optional?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類還提供了一個(gè)ifPresent()方法,該方法接收一個(gè)Consumer super T>函數(shù)式接口,一般用于將信息打印到控制臺(tái):
Optional?strOpt?=?Optional.of("Hello?World");
strOpt.ifPresent(System.out::println);
使用filter()方法過濾
filter()方法可用于判斷Optional對(duì)象是否滿足給定條件,一般用于條件過濾:
Optional?optional?=?Optional.of("[email protected]");
optional?=?optional.filter(str?->?str.contains("164"));
如何正確使用Optional
Optional?userOpt?=?Optional.ofNullable(user);
if?(userOpt.isPresent())?{
????User?user?=?userOpt.get();
????//?do?something...
}?else?{
????//?do?something...
}
坦白說,上面的代碼與我們之前的使用if語句判斷空值沒有任何區(qū)別,沒有起到Optional的正真作用:
if?(user?!=?null)?{
????//?do?something...
}?else?{
????//?do?something...
}
當(dāng)我們從之前版本切換到Java 8的時(shí)候,不應(yīng)該還按照之前的思維方式處理null值,Java 8提倡函數(shù)式編程,新增的許多API都可以用函數(shù)式編程表示,Optional類也是其中之一。這里有幾條關(guān)于Optional使用的建議:
盡量避免在程序中直接調(diào)用Optional對(duì)象的get()和isPresent()方法; 避免使用Optional類型聲明實(shí)體類的屬性;
第一條建議中直接調(diào)用get()方法是很危險(xiǎn)的做法,如果Optional的值為空,那么毫無疑問會(huì)拋出NullPointerException異常,而為了調(diào)用get()方法而使用isPresent()方法作為空值檢查,這種做法與傳統(tǒng)的用if語句塊做空值檢查沒有任何區(qū)別。
第二條建議避免使用Optional作為實(shí)體類的屬性,它在設(shè)計(jì)的時(shí)候就沒有考慮過用來作為類的屬性,如果你查看Optional的源代碼,你會(huì)發(fā)現(xiàn)它沒有實(shí)現(xiàn)java.io.Serializable接口,這在某些情況下是很重要的(比如你的項(xiàng)目中使用了某些序列化框架),使用了Optional作為實(shí)體類的屬性,意味著他們不能被序列化。
下面我們通過一些例子講解Optional的正確用法:
正確創(chuàng)建Optional對(duì)象
上面提到創(chuàng)建Optional對(duì)象有三個(gè)方法,empty()方法比較簡單,沒什么特別要說明的。主要是of()和ofNullable()方法。當(dāng)你很確定一個(gè)對(duì)象不可能為null的時(shí)候,應(yīng)該使用of()方法,否則,盡可能使用ofNullable()方法,比如:
public?static?void?method(Role?role)?{
????//?當(dāng)Optional的值通過常量獲得或者通過關(guān)鍵字new初始化,可以直接使用of()方法
????Optional?strOpt?=?Optional.of("Hello?World");
????Optional?userOpt?=?Optional.of(new?User());
????//?方法參數(shù)中role值不確定是否為null,使用ofNullable()方法創(chuàng)建
????Optional?roleOpt?=?Optional.ofNullable(role);
}
orElse()方法的使用
return?str?!=?null???str?:?"Hello?World"
上面的代碼表示判斷字符串str是否為空,不為空就返回,否則,返回一個(gè)常量。使用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?userOpt?=?Optional.ofNullable(user);
return?userOpt.map(User::getUserName)
????????????.map(String::toUpperCase)
????????????.orElse(null);
總結(jié)一下,新的Optional類讓我們可以以函數(shù)式編程的方式處理null值,拋棄了Java 8之前需要嵌套大量if-else代碼塊,使代碼可讀性有了很大的提高。

如有收獲,歡迎「分享?
」
「點(diǎn)贊
」「評(píng)論?
」
看完本文有收獲?請(qǐng)轉(zhuǎn)發(fā)分享給更多人
? 開發(fā)者全社區(qū)?
