根據(jù)Oracle文檔无畔,Optional是一個(gè)容器對(duì)象部凑,可以包含也可以不包含非null值。Optional在Java 8中引入,目的是解決 NullPointerExceptions的問題撩幽。本質(zhì)上,Optional是一個(gè)包裝器類箩艺,其中包含對(duì)其他對(duì)象的引用摸航。在這種情況下制跟,對(duì)象只是指向內(nèi)存位置的指針,并且也可以指向任何內(nèi)容酱虎。從其它角度看雨膨,Optional提供一種類型級(jí)解決方案來表示可選值而不是空引用。
在Optional之前
在Java 8之前读串,程序員將返回null而不是Optional聊记。這種方法有一些缺點(diǎn)。一種是沒有明確的方法來表示null可能是一個(gè)特殊值恢暖。相比之下排监,在API中返回Optional是明確的聲明,其中可能沒有值杰捂。如果我們要確保不會(huì)出現(xiàn)空指針異常舆床,則需要對(duì)每個(gè)引用進(jìn)行顯式的空檢查,如下所示嫁佳,我們都同意這是很多樣板挨队。
// Life before Optional
private void getIsoCode( User user){
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();
}
}
}
}
}
為了簡(jiǎn)化此過程,讓我們看一下如何使用Optional類蒿往,從創(chuàng)建和驗(yàn)證實(shí)例到使用它提供的不同方法并將其與返回相同類型的其他方法組合在一起盛垦,后者才是Optional的厲害之處。
Optional的特性
Optional類提供了大約10種方法瓤漏,我們可以使用它們來創(chuàng)建和使用Optional類腾夯,下面將介紹如何使用它們。
創(chuàng)建一個(gè)Optional類
這是用于創(chuàng)建可選實(shí)例的三種創(chuàng)建方法蔬充。
- static <T> [Optional]<T> [empty]()
返回一個(gè)空的Optional實(shí)例蝶俱。
// Creating an empty optional
Optional<String> empty = Optional.empty();
在返回一個(gè)空的{Optional}實(shí)例時(shí),Optional的值不存在饥漫。不過榨呆,這樣做可能很有誘惑力,如果對(duì)象為空趾浅,請(qǐng)避免與Option.empty()返回的實(shí)例的{==}比較 愕提。因?yàn)椴荒鼙WC它是一個(gè)單例馒稍,反之皿哨,應(yīng)該使用isPresent()。
- static <T> [Optional]<T> [of](T value)
返回特定的非空值Optional纽谒。
// Creating an optional using of
String name = "java";
Optional<String> opt = Optional.of(name);
靜態(tài)方法需要一個(gè)非null參數(shù)证膨;否則,將引發(fā)空指針異常鼓黔。因此央勒,如果我們不知道參數(shù)是否為null不见,那就是我們使用 ofNullable的時(shí)候,下面將對(duì)此進(jìn)行介紹崔步。
- static <T> [Optional]<T> [of](T value)
返回描述指定值的Optional稳吮,如果非空,則返回空值井濒。
// Possible null value
Optional<String> optional = Optional.ofNullable(name());
private String name(){
String name = "Java";
return (name.length() > 5) ? name : null;
}
如果我們傳入一個(gè)空引用灶似,它不會(huì)拋出異常,而是返回一個(gè)空的Optional對(duì)象:
所以這就是動(dòng)態(tài)或手動(dòng)創(chuàng)建Optional的三種方法瑞你。下一組方法用于檢查值的存在酪惭。
- 布爾值[isPresent]()
如果存在值,則返回true者甲;反之春感,返回false。如果所包含的對(duì)象不為null虏缸,則返回true鲫懒,反之返回false。通常在對(duì)對(duì)象執(zhí)行任何其他操作之前寇钉,先在Optional上調(diào)用此方法刀疙。
···
//ispresent
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){
//Do something, normally a get
}
···
- 布爾值[isEmpty()]
如果存在值,則返回false扫倡;否則谦秧,返回ture。這與isPresent 相反撵溃, 并且僅在Java 11及更高版本中可用疚鲤。
//isempty
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isEmpty()){
//Do something
}
- void [ifPresent]([Consumer]<? super [T]> consumer)
如果存在值,則使用該值調(diào)用指定的使用者缘挑;否則集歇,什么都不做。
如果您不熟悉Java 8语淘,那么您可能會(huì)想知道:什么是消費(fèi)者诲宇?簡(jiǎn)單來說,消費(fèi)者是一種接受參數(shù)且不返回任何內(nèi)容的方法惶翻。當(dāng)使用 ifPresent時(shí)姑蓝,這個(gè)方法就是一石二鳥。我們可以執(zhí)行值存在性檢查并使用一種方法執(zhí)行預(yù)期的操作吕粗,如下所示纺荧。
//ifpresent
Optional<String> optional1 = Optional.of("javaone");
optional1.ifPresent(s -> System.out.println(s.length()));
可選類提供了另一組用于獲取可選值的方法。
- T[get]()
如果此Optional中存在值,則返回該值宙暇,否則拋出 NoSuchElementException输枯。在這之后,我們想要的是存儲(chǔ)在Optional中的值占贫,我們可以通過get()來獲取它桃熄。但是,當(dāng)該值為null時(shí)型奥,此方法將引發(fā)異常蜻拨。這就需要 orElse() 方法來緊急救援。
//get
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){
String value = optional1.get();
}
- [T ][orElse]([T]其他)
返回值(如果存在)桩引;反之缎讼,返回其他。
該 orElse() 方法用于檢索包裝在Optional實(shí)例內(nèi)的值坑匠。它采用一個(gè)充當(dāng)默認(rèn)值的參數(shù)血崭。該 orElse() 方法返回包裝的值(如果存在)及其參數(shù),反之:
//orElse
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("default_name");
如果這還不夠厘灼,那么Optional類將繼續(xù)提供另一種獲取值的方法夹纫,即使該方法的null稱為 orElseGet()。
- [T][orElseGet]([Supplier]<? extends [T]> other)
返回值(如果存在)设凹;否則舰讹,調(diào)用other并返回該調(diào)用的結(jié)果。
該orElseGet() 方法類似于 orElse()闪朱。但是月匣,如果沒有Optional值,則不采用返回值奋姿,而是采用供應(yīng)商功能接口锄开,該接口將被調(diào)用并返回調(diào)用的值:
//orElseGet
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
那么,orElse() 和orElseGet()之間有什么區(qū)別称诗。
乍一看萍悴,這兩種方法似乎具有相同的效果。但是寓免,事實(shí)并非如此癣诱。讓我們創(chuàng)建一些示例,以突出兩者之間的相似性和行為差異袜香。
首先撕予,讓我們看看它們?cè)趯?duì)象為空時(shí)的行為:
String text = null;
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultValue);
defaultText = Optional.ofNullable(text).orElse(getDefaultValue());
public String getDefaultValue() {
System.out.println("Getting Default Value");
return "Default Value";
}
在上面的示例中,我們?cè)贠ptional對(duì)象中包裝了一個(gè)空文本困鸥,然后嘗試使用兩種方法中的每一種來獲取包裝后的值嗅蔬。副作用如下:
Getting default value...
Getting default value...
在每種情況下都會(huì)調(diào)用默認(rèn)方法。碰巧的是疾就,當(dāng)不存在包裝的值時(shí)澜术,兩者 orElse() 和的 orElseGet() 工作方式完全相同。
現(xiàn)在猬腰,讓我們運(yùn)行另一個(gè)該值存在測(cè)試鸟废,理想情況下,甚至不應(yīng)創(chuàng)建默認(rèn)值:
在這個(gè)簡(jiǎn)單的示例中姑荷,創(chuàng)建默認(rèn)對(duì)象不會(huì)花費(fèi)很多成本盒延,因?yàn)镴VM知道如何處理此類對(duì)象。但是鼠冕,當(dāng)諸如此類的方法 default 必須進(jìn)行Web服務(wù)調(diào)用或者查詢數(shù)據(jù)庫時(shí)添寺,則成本變得非常明顯。
使用Optional最佳實(shí)踐
就像編程語言的任何其他功能一樣懈费,它可以正確使用或被濫用计露。為了了解使用Optional類的最佳方法,需要了解以下內(nèi)容:
1.它解決的問題
Optional的方法是嘗試通過增加構(gòu)建更具表現(xiàn)力的API的可能性來減少Java系統(tǒng)中空指針異常的情況憎乙,這些API解釋了有時(shí)缺少返回值的可能性票罐。
如果從一開始就存在Optional,那么大多數(shù)庫和應(yīng)用程序可能會(huì)更好地處理缺少的返回值泞边,從而減少了空指針異常的數(shù)量以及總體上的錯(cuò)誤總數(shù)该押。
2.它不解決的問題
Optional并不意味著是一種避免所有類型的空指針的機(jī)制。例如阵谚,它仍然必須測(cè)試方法和構(gòu)造函數(shù)的強(qiáng)制輸入?yún)?shù)蚕礼。
像使用null時(shí)一樣,Optional不能幫助傳達(dá)缺失值的含義梢什。以類似的方式闻牡,null可能意味著很多不同的東西(找不到值等),因此缺少Optional值也可以绳矩。
該方法的調(diào)用方仍然需要檢查該方法的JavaDoc以理解缺省選項(xiàng)的含義罩润,以便正確地處理它。
同樣翼馆,以一種類似的方式割以,可以將檢查的異常捕獲在一個(gè)空塊中,沒有什么阻止調(diào)用方進(jìn)行調(diào)用 get() 并繼續(xù)進(jìn)行应媚。
3.何時(shí)使用
Optional的預(yù)期用途主要是作為返回類型严沥。獲取此類型的實(shí)例后,可以提取該值(如果存在)或提供其他行為(如果不存在)中姜。
Optional類的一個(gè)非常有用的用例是將其與流或返回Optional值以構(gòu)建流暢的API的其他方法結(jié)合消玄。請(qǐng)參見下面的代碼段
User user = users.stream().findFirst().orElse(new User("default", "1234"));
4.什么時(shí)候不使用
a)不要將其用作類中的字段跟伏,因?yàn)樗豢尚蛄谢?/p>
如果確實(shí)需要序列化包含Optional值的對(duì)象,則Jackson庫提供了將Optionals視為普通對(duì)象的支持翩瓜。這意味著Jackson將空對(duì)象視為空受扳,將具有值的對(duì)象視為包含該值的字段⊥玫可以在jackson-modules-java8項(xiàng)目中找到此功能勘高。
b)不要將其用作構(gòu)造函數(shù)和方法的參數(shù),因?yàn)檫@會(huì)導(dǎo)致不必要的復(fù)雜代碼坟桅。
User user = new User("john@gmail.com", "1234", Optional.empty());
本人創(chuàng)業(yè)團(tuán)隊(duì)產(chǎn)品MadPecker华望,主要做BUG管理、測(cè)試管理仅乓、應(yīng)用分發(fā)
網(wǎng)址:[www.madpecker.com]赖舟,有需要的朋友歡迎試用、體驗(yàn)夸楣!
本文為MadPecker團(tuán)隊(duì)技術(shù)人員譯制建蹄,轉(zhuǎn)載請(qǐng)標(biāo)明出處