Java8 Optional用法和最佳實(shí)踐

根據(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)建方法蔬充。

  1. 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()。

  1. 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)行介紹崔步。

  1. 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的三種方法瑞你。下一組方法用于檢查值的存在酪惭。

  1. 布爾值[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

}
···

  1. 布爾值[isEmpty()]

如果存在值,則返回false扫倡;否則谦秧,返回ture。這與isPresent 相反撵溃, 并且僅在Java 11及更高版本中可用疚鲤。

 //isempty

Optional<String> optional1 = Optional.of("javaone");

if (optional1.isEmpty()){

  //Do something

}
  1. 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()));

可選類提供了另一組用于獲取可選值的方法。

  1. 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();
}
  1. [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()。

  1. [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)明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市裕偿,隨后出現(xiàn)的幾起案子洞慎,更是在濱河造成了極大的恐慌,老刑警劉巖嘿棘,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劲腿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鸟妙,警方通過查閱死者的電腦和手機(jī)焦人,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來重父,“玉大人花椭,你說我怎么就攤上這事》课纾” “怎么了矿辽?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長郭厌。 經(jīng)常有香客問我袋倔,道長,這世上最難降的妖魔是什么折柠? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任宾娜,我火速辦了婚禮,結(jié)果婚禮上扇售,老公的妹妹穿的比我還像新娘前塔。我一直安慰自己嚣艇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布华弓。 她就那樣靜靜地躺著食零,像睡著了一般。 火紅的嫁衣襯著肌膚如雪该抒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天顶燕,我揣著相機(jī)與錄音凑保,去河邊找鬼。 笑死涌攻,一個(gè)胖子當(dāng)著我的面吹牛欧引,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恳谎,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼芝此,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了因痛?” 一聲冷哼從身側(cè)響起婚苹,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸵膏,沒想到半個(gè)月后膊升,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谭企,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年廓译,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片债查。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡非区,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盹廷,到底是詐尸還是另有隱情征绸,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布俄占,位于F島的核電站歹垫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏颠放。R本人自食惡果不足惜排惨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碰凶。 院中可真熱鬧暮芭,春花似錦鹿驼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瑞筐,卻和暖如春凄鼻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背聚假。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工块蚌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膘格。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓峭范,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘪贱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子纱控,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容