(轉(zhuǎn))Java 8 Optional類深度解析

轉(zhuǎn)載自:https://www.cnblogs.com/xingzc/p/5778090.html

思考: 調(diào)用一個(gè)方法得到了返回值卻不能直接將返回值作為參數(shù)去調(diào)用別的方法欺栗。

原來解決方案: 我們首先要判斷這個(gè)返回值是否為null养篓,只有在非空的前提下才能將其作為其他方法的參數(shù)。這正是一些類似Guava的外部API試圖解決的問題。

       ?一些JVM編程語言比如Scala背镇、Ceylon等已經(jīng)將對(duì)在核心API中解決了這個(gè)問題殿较。


新版本的Java,比如Java 8引入了一個(gè)新的Optional類澜躺。Optional類的Javadoc描述如下:

這是一個(gè)可以為null的容器對(duì)象蝉稳。如果值存在則isPresent()方法會(huì)返回true,調(diào)用get()方法會(huì)返回該對(duì)象掘鄙。

本文會(huì)逐個(gè)探討Optional類包含的方法耘戚,并通過一兩個(gè)示例展示如何使用。

of

為非null的值創(chuàng)建一個(gè)Optional操漠。

  of方法通過工廠方法創(chuàng)建Optional類收津。需要注意的是,創(chuàng)建對(duì)象時(shí)傳入的參數(shù)不能為null浊伙。如果傳入?yún)?shù)為null撞秋,則拋出NullPointerException 。

//調(diào)用工廠方法創(chuàng)建Optional實(shí)例

Optional<String> name = Optional.of("Sanaulla");

//傳入?yún)?shù)為null吧黄,拋出NullPointerException.

Optional<String> someNull = Optional.of(null);

ofNullable

為指定的值創(chuàng)建一個(gè)Optional部服,如果指定的值為null,則返回一個(gè)空的Optional拗慨。

ofNullable與of方法相似廓八,唯一的區(qū)別是可以接受參數(shù)為null的情況。示例如下:

//下面創(chuàng)建了一個(gè)不包含任何值的Optional實(shí)例

//例如赵抢,值為'null'

Optional empty = Optional.ofNullable(null);

isPresent

非常容易理解

如果值存在返回true剧蹂,否則返回false。

類似下面的代碼:

//isPresent方法用來檢查Optional實(shí)例中是否包含值

if?(name.isPresent()) {

??//在Optional實(shí)例內(nèi)調(diào)用get()返回已存在的值

??System.out.println(name.get());//輸出Sanaulla

}

get

如果Optional有值則將其返回烦却,否則拋出NoSuchElementException宠叼。

上面的示例中,get方法用來得到Optional實(shí)例中的值。下面我們看一個(gè)拋出NoSuchElementException的例子:

//執(zhí)行下面的代碼會(huì)輸出:No value present

try?{

??//在空的Optional實(shí)例上調(diào)用get()冒冬,拋出NoSuchElementException

??System.out.println(empty.get());

} catch?(NoSuchElementException ex) {

??System.out.println(ex.getMessage());

}

ifPresent  

如果Optional實(shí)例有值則為其調(diào)用consumer伸蚯,否則不做處理

要理解ifPresent方法,首先需要了解Consumer類简烤。簡(jiǎn)答地說剂邮,Consumer類包含一個(gè)抽象方法。該抽象方法對(duì)傳入的值進(jìn)行處理横侦,但沒有返回值挥萌。

Java8支持不用接口直接通過lambda表達(dá)式傳入?yún)?shù)。

如果Optional實(shí)例有值枉侧,調(diào)用ifPresent()可以接受接口段或lambda表達(dá)式引瀑。類似下面的代碼:

//ifPresent方法接受lambda表達(dá)式作為參數(shù)。

//lambda表達(dá)式對(duì)Optional的值調(diào)用consumer進(jìn)行處理榨馁。

name.ifPresent((value) -> {

??System.out.println("The length of the value is: "?+ value.length());

return “adf”;//此處不可以有返回值

});


orElse

如果有值則將其返回憨栽,否則返回指定的其它值。

如果Optional實(shí)例有值則將其返回辆影,否則返回orElse方法傳入的參數(shù)徒像。示例如下:

//如果值不為null,orElse方法返回Optional實(shí)例的值蛙讥。

//如果為null锯蛀,返回傳入的消息。

//輸出:There is no value present!

System.out.println(empty.orElse("There is no value present!"));

//輸出:Sanaulla

System.out.println(name.orElse("There is some value!"));

orElseGet

orElseGet與orElse方法類似次慢,區(qū)別在于得到的默認(rèn)值旁涤。orElse方法將傳入的字符串作為默認(rèn)值,orElseGet方法可以接受Supplier接口的實(shí)現(xiàn)用來生成默認(rèn)值迫像。示例如下:

//orElseGet與orElse方法類似劈愚,區(qū)別在于orElse傳入的是默認(rèn)值,

//orElseGet可以接受一個(gè)lambda表達(dá)式生成默認(rèn)值闻妓。

//輸出:Default Value

System.out.println(empty.orElseGet(() -> "Default Value"));

//輸出:Sanaulla

System.out.println(name.orElseGet(() -> "Default Value"));

orElseThrow

如果有值則將其返回菌羽,否則拋出supplier接口創(chuàng)建的異常。

在orElseGet方法中由缆,我們傳入一個(gè)Supplier接口注祖。然而,在orElseThrow中我們可以傳入一個(gè)lambda表達(dá)式或方法均唉,如果值不存在來拋出異常是晨。示例如下:

try?{

??//orElseThrow與orElse方法類似。與返回默認(rèn)值不同舔箭,

??//orElseThrow會(huì)拋出lambda表達(dá)式或方法生成的異常


??empty.orElseThrow(ValueAbsentException::new);

} catch?(Throwable ex) {

??//輸出: No value present in the Optional instance

??System.out.println(ex.getMessage());

}

ValueAbsentException定義如下:

class?ValueAbsentException extends?Throwable {

??public?ValueAbsentException() {

????super();

??}


??public?ValueAbsentException(String msg) {

????super(msg);

??}


??@Override

??public?String getMessage() {

????return?"No value present in the Optional instance";

??}

}

map

map方法文檔說明如下:

如果有值罩缴,則對(duì)其執(zhí)行調(diào)用mapping函數(shù)得到返回值。如果返回值不為null,則創(chuàng)建包含mapping返回值的Optional作為map方法返回值箫章,否則返回空Optional烙荷。

map方法用來對(duì)Optional實(shí)例的值執(zhí)行一系列操作。通過一組實(shí)現(xiàn)了Function接口的lambda表達(dá)式傳入操作炉抒。map方法示例如下:

//map方法執(zhí)行傳入的lambda表達(dá)式參數(shù)對(duì)Optional實(shí)例的值進(jìn)行修改奢讨。

//為lambda表達(dá)式的返回值創(chuàng)建新的Optional實(shí)例作為map方法的返回值。

Optional<String> upperName = name.map((value) -> value.toUpperCase());

System.out.println(upperName.orElse("No value found"));

flatMap

如果有值焰薄,為其執(zhí)行mapping函數(shù)返回Optional類型返回值,否則返回空Optional扒袖。flatMap與map(Funtion)方法類似塞茅,區(qū)別在于flatMap中的mapper返回值必須是Optional。調(diào)用結(jié)束時(shí)季率,flatMap不會(huì)對(duì)結(jié)果用Optional封裝野瘦。

flatMap方法與map方法類似,區(qū)別在于mapping函數(shù)的返回值不同飒泻。map方法的mapping函數(shù)返回值可以是任何類型T鞭光,而flatMap方法的mapping函數(shù)必須是Optional。

參照map函數(shù)泞遗,使用flatMap重寫的示例如下:

//flatMap與map(Function)非常類似惰许,區(qū)別在于傳入方法的lambda表達(dá)式的返回類型。

//map方法中的lambda表達(dá)式返回值可以是任意類型史辙,在map函數(shù)返回之前會(huì)包裝為Optional汹买。

//但flatMap方法中的lambda表達(dá)式返回值必須是Optionl實(shí)例。

upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));

System.out.println(upperName.orElse("No value found"));//輸出SANAULLA

filter

filter個(gè)方法通過傳入限定條件對(duì)Optional實(shí)例的值進(jìn)行過濾聊倔。文檔描述如下:

如果有值并且滿足斷言條件返回包含該值的Optional晦毙,否則返回空Optional。

讀到這里耙蔑,可能你已經(jīng)知道如何為filter方法傳入一段代碼见妒。是的,這里可以傳入一個(gè)lambda表達(dá)式甸陌。對(duì)于filter函數(shù)我們應(yīng)該傳入實(shí)現(xiàn)了Predicate接口的lambda表達(dá)式须揣。如果你不熟悉Predicate接口,可以參考這篇文章邀层。

現(xiàn)在我來看看filter的各種用法返敬,下面的示例介紹了滿足限定條件和不滿足兩種情況:

//filter方法檢查給定的Option值是否滿足某些條件。

//如果滿足則返回同一個(gè)Option實(shí)例寥院,否則返回空Optional劲赠。

Optional<String> longName = name.filter((value) -> value.length() > 6);

System.out.println(longName.orElse("The name is less than 6 characters"));//輸出Sanaulla


//另一個(gè)例子是Optional值不滿足filter指定的條件。

Optional<String> anotherName = Optional.of("Sana");

Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);

//輸出:name長(zhǎng)度不足6字符

System.out.println(shortName.orElse("The name is less than 6 characters"));

以上,我們介紹了Optional類的各個(gè)方法凛澎。下面通過一個(gè)完整的示例對(duì)用法集中展示:

public?class?OptionalDemo {


??public?static?void?main(String[] args) {

????//創(chuàng)建Optional實(shí)例霹肝,也可以通過方法返回值得到。

????Optional<String> name = Optional.of("Sanaulla");


????//創(chuàng)建沒有值的Optional實(shí)例塑煎,例如值為'null'

????Optional empty = Optional.ofNullable(null);


????//isPresent方法用來檢查Optional實(shí)例是否有值沫换。

????if?(name.isPresent()) {

??????//調(diào)用get()返回Optional值。

??????System.out.println(name.get());

????}


????try?{

??????//在Optional實(shí)例上調(diào)用get()拋出NoSuchElementException最铁。

??????System.out.println(empty.get());

????} catch?(NoSuchElementException ex) {

??????System.out.println(ex.getMessage());

????}


????//ifPresent方法接受lambda表達(dá)式參數(shù)讯赏。

????//如果Optional值不為空,lambda表達(dá)式會(huì)處理并在其上執(zhí)行操作冷尉。

????name.ifPresent((value) -> {

??????System.out.println("The length of the value is: "?+ value.length());

????});


????//如果有值orElse方法會(huì)返回Optional實(shí)例漱挎,否則返回傳入的錯(cuò)誤信息。

????System.out.println(empty.orElse("There is no value present!"));

????System.out.println(name.orElse("There is some value!"));


????//orElseGet與orElse類似雀哨,區(qū)別在于傳入的默認(rèn)值磕谅。

????//orElseGet接受lambda表達(dá)式生成默認(rèn)值。

????System.out.println(empty.orElseGet(() -> "Default Value"));

????System.out.println(name.orElseGet(() -> "Default Value"));


????try?{

??????//orElseThrow與orElse方法類似雾棺,區(qū)別在于返回值膊夹。

??????//orElseThrow拋出由傳入的lambda表達(dá)式/方法生成異常。

??????empty.orElseThrow(ValueAbsentException::new);

????} catch?(Throwable ex) {

??????System.out.println(ex.getMessage());

????}


????//map方法通過傳入的lambda表達(dá)式修改Optonal實(shí)例默認(rèn)值捌浩。

????//lambda表達(dá)式返回值會(huì)包裝為Optional實(shí)例放刨。

????Optional<String> upperName = name.map((value) -> value.toUpperCase());

????System.out.println(upperName.orElse("No value found"));


????//flatMap與map(Funtion)非常相似,區(qū)別在于lambda表達(dá)式的返回值嘉栓。

????//map方法的lambda表達(dá)式返回值可以是任何類型宏榕,但是返回值會(huì)包裝成Optional實(shí)例。

????//但是flatMap方法的lambda返回值總是Optional類型侵佃。

????upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));

????System.out.println(upperName.orElse("No value found"));


????//filter方法檢查Optiona值是否滿足給定條件麻昼。

????//如果滿足返回Optional實(shí)例值,否則返回空Optional馋辈。

????Optional<String> longName = name.filter((value) -> value.length() > 6);

????System.out.println(longName.orElse("The name is less than 6 characters"));


????//另一個(gè)示例抚芦,Optional值不滿足給定條件。

????Optional<String> anotherName = Optional.of("Sana");

????Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);

????System.out.println(shortName.orElse("The name is less than 6 characters"));


??}


}

上述代碼輸出如下:

Sanaulla

No value present

The length of the value is: 8

There is no value present!

Sanaulla

Default Value

Sanaulla

No value present in?the Optional instance

SANAULLA

SANAULLA

Sanaulla

The name is less?than 6 characters

實(shí)例展示

假如有一個(gè)Person 對(duì)象迈螟,它有一個(gè)Address 屬性叉抡,而Address屬性還嵌套一個(gè)validFrom的日期。所有的值都可能是null答毫。

好了褥民,現(xiàn)在我們來判斷一下Person 的這些屬性是否是Valid的。

privateboolean?validAddress(NullPerson person){

if(person!=null){

if(person.getAddress()!=null){

final?Instant validFrom=?person.getAddress().getValidFrom();

return?validFrom!=null&&?validFrom.isBefore(now());

}else

returnfalse;

}else

returnfalse;

}

或者也可以這么寫:

return?person!=null&&

person.getAddress()!=null&&

person.getAddress().getValidFrom()!=null&&

person.getAddress().getValidFrom().isBefore(now());

總之 都不是很好看洗搂。

但如果這些屬性都是Optional消返,那么看起來會(huì)稍微舒服一點(diǎn)载弄。

class?Person{

privatefinal?Optional?address;

public?Optional?getAddress(){

return?address;

}

//...

}

class?Address{

privatefinal?Optional?validFrom;

public?Optional?getValidFrom(){

return?validFrom;

}

//...

}

以下代碼就實(shí)現(xiàn)了一樣的判斷:

return?person.

flatMap(Person::getAddress).

flatMap(Address::getValidFrom).

filter(x->?x.before(now())).

isPresent();

使用Optional以后,NullPointerException 就從此消失了撵颊。

將一個(gè)Optional轉(zhuǎn)為L(zhǎng)ist或者Set

Optional是一個(gè)集合宇攻,雖然里面只有0或者1個(gè)元素,但它一樣是一個(gè)集合倡勇。如果要轉(zhuǎn)為L(zhǎng)ist或者Set逞刷,一般的寫法可以是:

publicstatic?List?toList(Optional?option){

return?option.

map(Collections::singletonList).

orElse(Collections.emptyList());

}

或者更傳統(tǒng)的寫法:

publicstatic?List?toList(Optional?option){

if(option.isPresent())

returnCollections.singletonList(option.get());

else

returnCollections.emptyList();

}

但是在java8里,其實(shí)只需要這么寫:

importstatic?java.util.stream.Collectors.*;

//轉(zhuǎn)為L(zhǎng)ist

List?list=?collect(opt, toList());

//轉(zhuǎn)為Set

Set??set=?collect(opt, toSet());

本文代碼內(nèi)容來自于:http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妻熊,一起剝皮案震驚了整個(gè)濱河市夸浅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扔役,老刑警劉巖题篷,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異厅目,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)法严,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門损敷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人深啤,你說我怎么就攤上這事拗馒。” “怎么了溯街?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵诱桂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我呈昔,道長(zhǎng)挥等,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任堤尾,我火速辦了婚禮肝劲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘郭宝。我一直安慰自己辞槐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布粘室。 她就那樣靜靜地躺著榄檬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衔统。 梳的紋絲不亂的頭發(fā)上鹿榜,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天海雪,我揣著相機(jī)與錄音,去河邊找鬼犬缨。 笑死喳魏,一個(gè)胖子當(dāng)著我的面吹牛闯狱,可吹牛的內(nèi)容都是我干的奶稠。 我是一名探鬼主播岁歉,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼甘桑,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼粗蔚!你這毒婦竟也來了段标?” 一聲冷哼從身側(cè)響起翔脱,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤琢歇,失蹤者是張志新(化名)和其女友劉穎焚碌,沒想到半個(gè)月后畦攘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡十电,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年知押,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹃骂。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡台盯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畏线,到底是詐尸還是另有隱情静盅,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布寝殴,位于F島的核電站蒿叠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蚣常。R本人自食惡果不足惜市咽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望史隆。 院中可真熱鬧魂务,春花似錦、人聲如沸泌射。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熔酷。三九已至孤紧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拒秘,已是汗流浹背号显。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工臭猜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人押蚤。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓蔑歌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親揽碘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子次屠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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

  • Java 8自Java 5(發(fā)行于2004)以來最具革命性的版本。Java 8 為Java語言雳刺、編譯器劫灶、類庫、開發(fā)...
    huoyl0410閱讀 620評(píng)論 1 2
  • Java 8自Java 5(發(fā)行于2004)以來最具革命性的版本掖桦。Java 8 為Java語言本昏、編譯器、類庫枪汪、開發(fā)...
    誰在烽煙彼岸閱讀 887評(píng)論 0 4
  • 姓名:崔升 學(xué)號(hào):14020120005 轉(zhuǎn)載自:http://www.jb51.net/article/483...
    冬瓜小正太閱讀 4,409評(píng)論 0 0
  • 前言:Java 8 已經(jīng)發(fā)布很久了涌穆,很多報(bào)道表明Java 8 是一次重大的版本升級(jí)。在Java Code Geek...
    糖寶_閱讀 1,321評(píng)論 1 1
  • 【好大一坨尿啊】 小米說話比較晚雀久,兩歲才會(huì)說一句短話蒲犬,現(xiàn)在兩歲四個(gè)月,詞匯量一天天豐富和充盈起來岸啡,米媽現(xiàn)在最喜歡的...
    愚兒米媽閱讀 266評(píng)論 0 0