Optional的學(xué)習(xí)與實(shí)戰(zhàn)
整片文章大部分內(nèi)容來(lái)自java8實(shí)戰(zhàn)這本書(shū),我在這里也是將自己的學(xué)習(xí)過(guò)程記錄下來(lái),并且整理成筆記給需要的人提供一個(gè)方便,在學(xué)習(xí)的過(guò)程中主要有以下幾點(diǎn)疑惑:
不明白Optional的作用是什么?
不清楚在什么情況下使用Optional?
面對(duì)多層嵌套的情況Optional如何拆解?
Optional和Stream之間的關(guān)系?
寫(xiě)在前面
作為一個(gè)java開(kāi)發(fā)程序猿如果說(shuō)你沒(méi)有遇到過(guò)空指針的問(wèn)題,那真的是太神奇了,在我們的工作中,常常會(huì)想盡一切辦法避免空指針的.事實(shí)上也有人曾經(jīng)提出空引用的想法,就是對(duì)null依然進(jìn)行引用,但是這種做法所帶來(lái)的可怕影響就是會(huì)在后期帶啦大量的NullPrinterException異常.
本文的目的是使用Optional來(lái)取代null,并且使用Optional來(lái)去除代碼中大量的null檢查從而提高我們程序的可讀性和代碼的健壯性.
Null帶來(lái)的種種問(wèn)題
錯(cuò)誤之源:NullPointExpection是java開(kāi)發(fā)中最經(jīng)常遭遇的異常.
是你的代碼膨脹:頁(yè)面中充斥著過(guò)多而null檢查,是代碼的可讀性大大降低.
自身毫無(wú)意義:自身沒(méi)有任何意義,這本身就是一種錯(cuò)誤的建模.
破壞了java的哲學(xué)性:java一直試圖讓人們避免接觸指針的問(wèn)題,但是null是唯一的例外
在java類(lèi)型系統(tǒng)上開(kāi)了一個(gè)口子:null并不屬于任何類(lèi)型,這意味著他可以賦值給任意變量.
其他語(yǔ)言中null的替代品
在Groovy中可以通過(guò)安全導(dǎo)航操作符(Safe Navigation Operator,標(biāo)記為?)
carInsuranceName= person?.car?.insurance?.name
1
在使用變量是可能出現(xiàn)null的情況,但是在這種情況下不會(huì)拋出空指針的異常,而是將null沿著調(diào)用鏈一直傳遞下去.最終返回一個(gè)null.
在java8中引入了Optional的概念設(shè)置了一個(gè)新的類(lèi)java.util.Option的類(lèi)型,從而對(duì)可能出現(xiàn)null的對(duì)象進(jìn)行建模
Optional類(lèi)入門(mén)
如果我們需要使用一個(gè)對(duì)象,這個(gè)對(duì)象中可能存在null,我們可以聲明其為Optional類(lèi)型,變量存在是返回的是封裝的對(duì)象,當(dāng)變量不存在使返回的是一個(gè)空的Optional對(duì)象.在語(yǔ)法上你可以把這個(gè)當(dāng)做一回事,但是實(shí)際中他們之間存才非常大的差別.如果你引用的是一個(gè)null,一定會(huì)觸發(fā)NullPointException
,如果使用Optional.empty()就完全沒(méi)有事.
**使用Optional而不是null是一個(gè)非常重要而又實(shí)際的語(yǔ)義區(qū)別.
使用Optional對(duì)你的代碼進(jìn)行重構(gòu).
public class EssayCompositeQuestionDot {
? ? /**
? ? * paper name
? ? */
? ? private String paperName;
? ? /**
? ? * paper id
? ? */
? ? private int paperId;
? ? /**
? ? * Check to see if the object highlighted
? ? * In this class 'flag' default 'true'
? ? */
? ? private boolean flag = true;
? ? private List<Optional<StemList>> stemList = new LinkedList<>();
? ? private List<Optional<MaterialList>> materialList = new LinkedList<>();
? ? @Data
? ? @NoArgsConstructor
? ? @AllArgsConstructor
? ? public class MaterialList {
? ? ? ? /**
? ? ? ? * material id
? ? ? ? */
? ? ? ? private Integer id;
? ? ? ? /**
? ? ? ? * material content
? ? ? ? */
? ? ? ? private String content;
? ? ? ? /**
? ? ? ? * Check to see if the object highlighted
? ? ? ? */
? ? ? ? private boolean flag = false;
? ? }
? ? @Data
? ? @NoArgsConstructor
? ? @AllArgsConstructor
? ? public class StemList {
? ? ? ? /**
? ? ? ? * stem baseId
? ? ? ? */
? ? ? ? private Integer baseId;
? ? ? ? /**
? ? ? ? * stem detailId
? ? ? ? */
? ? ? ? private Integer detailId;
? ? ? ? /**
? ? ? ? * stem content
? ? ? ? */
? ? ? ? private String content;
? ? ? ? /**
? ? ? ? * Check to see if the object highlighted
? ? ? ? */
? ? ? ? private boolean flag = false;
? ? }
創(chuàng)建Optioanl對(duì)象
Optional提供了集中靜態(tài)方法供我們直接使用
聲明一個(gè)空的Optional
直接調(diào)用靜態(tài)工廠的方法Optional.empty創(chuàng)建一個(gè)對(duì)象
? ? Optional<EssayQuestionGroupResponse> empty = Optional.empty();
1
根據(jù)非空值創(chuàng)建Optional
直接調(diào)用講臺(tái)方法Optional.of,依據(jù)一個(gè)非空值創(chuàng)建一個(gè)Optional對(duì)象
? ? Optional<EssayQuestionGroupResponse> essayQuestionGroupResponse = Optional.of(new EssayQuestionGroupResponse());
可接受null的Optional
使用講臺(tái)工廠方法Optional.ofNullable創(chuàng)建一個(gè)允許null值得Optional對(duì)象
EssayQuestionGroupResponse essayQuestionGroupResponseNull = new EssayQuestionGroupResponse();
? ? ? ? Optional<EssayQuestionGroupResponse> essayQuestionGroupResponseNullOptional = Optional.ofNullable(essayQuestionGroupResponseNull);
獲取Optional中的對(duì)象
我們將對(duì)象放到了Optional中,他們本身提供了一個(gè)get方法,但是如果我們不按照約定進(jìn)行g(shù)et那么他依然會(huì)拋出一個(gè)NullException
? ? EssayQuestionGroupResponse essayQuestionGroupResponseNull = new EssayQuestionGroupResponse();
? ? ? ? System.out.println(essayQuestionGroupResponse.map(EssayQuestionGroupResponse::getGroupId).get());
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at com.huatu.search.optional.OptionalGetTest.main(OptionalGetTest.java:19)
使用map從optional對(duì)象中提取和轉(zhuǎn)化值
從對(duì)象中提取信息這是很常見(jiàn)的模式,Optional提供了一個(gè)map方法,他的工作方式如下.
essayQuestionGroupResponse.map(EssayQuestionGroupResponse::getGroupId);
同學(xué)們也很容易發(fā)現(xiàn),如果想要獲取嵌套對(duì)象,讓然使用map是不可取的,這里需要使用flatMap
essayQuestionGroupResponse
? ? ? ? .flatMap(EssayQuestionGroupResponse::getMaterialList)
? ? ? ? .map(EssayQuestionGroupResponse.Material::getMaterialContent).orElse("null"))
在最后使用orElse如果在調(diào)用鏈中的認(rèn)識(shí)一環(huán)出現(xiàn)空指針則會(huì)返回orElse中的內(nèi)容
? ? Optional<EssayQuestionGroupResponse> essayQuestionGroupResponse = Optional.of(new EssayQuestionGroupResponse());
? ? ? ? //這里會(huì)出現(xiàn)空值,以后在研究
? ? ? ? System.out.println(essayQuestionGroupResponse
? ? ? ? ? ? ? ? .flatMap(EssayQuestionGroupResponse::getMaterialList)
? ? ? ? ? ? ? ? .map(Material::getMaterialContent).orElse("null"));
? ? }
默認(rèn)行為以及街引用Optional對(duì)象
再生產(chǎn)中有如下幾種方式獲取到Optional中的對(duì)象
get()是最簡(jiǎn)單的方法,并且不安全,如果變量存在直接返回封裝的變量值,否做就跑出一個(gè)NoSuchElementException異常 .
orElse(T other)當(dāng)Optional中不包含值得時(shí)候提供一個(gè)默認(rèn)值.
orElseGet(Supplier<? extends T>)是上一個(gè)方法的延時(shí)調(diào)用版,需要提高性能可以使用此方法.
orElseThrow(Supplier<? extends exceptionSupplier>)如果對(duì)象為空的時(shí)候回拋出一個(gè)異常.
ifPresent(Consumer<? super T>) 讓變量在存在的時(shí)候作為一個(gè)消費(fèi)者傳入一個(gè)方法否則不執(zhí)行任何操作.
實(shí)戰(zhàn)實(shí)例
有效的使用Optional類(lèi)意味著在需要處理潛在缺失值得時(shí)候可以進(jìn)行全面的反思了.
用Optional封裝可能為null的值
現(xiàn)存的很多方法都是通過(guò)返回一個(gè)null來(lái)表示沒(méi)有這個(gè)值,就好比我們經(jīng)常使用的map.get()方法,如果該map中不存在對(duì)應(yīng)屬性則會(huì)返回一個(gè)null.這是用Optional將代碼封裝起來(lái)就可以減少if-else的判斷.
Optional<String> value=Optional.ofNullable(map.get("key"));
每次在獲取潛為null的對(duì)象時(shí)都可以調(diào)用這個(gè)方法.
異常與Optional的對(duì)比
由于某種原因,函數(shù)無(wú)法返回某個(gè)值,這是除了返回null,JavaAPI通常的做法是拋出一個(gè)異常.這種情況比較典型的例子就是Integer.parseInt(String),如果轉(zhuǎn)化失敗則會(huì)拋出一個(gè)NumberFormatException,這種情況和之前的null相比唯一不同的處理方法就是使用try/catch,而之前是使用if/else進(jìn)行判斷.這時(shí)候我們可以用如下的方式完成這個(gè)功能.
public static Optional<Integer> stringToInt (String s) {
? ? try {
? ? ? ? return Optional.of(Integer.parseInt(s);
? ? } catch (NumberFormatException e) {
? ? ? ? return Optional.empty();
? ? }
}
我們可以將多中類(lèi)似的方法封裝到一個(gè)工具類(lèi)中進(jìn)行使用.
總結(jié)
整片文章大部分內(nèi)容來(lái)自java8實(shí)戰(zhàn)這本書(shū),我在這里也是將自己的學(xué)習(xí)過(guò)程記錄下來(lái),并且整理成筆記給需要的人提供一個(gè)方便,在學(xué)習(xí)的過(guò)程中主要有以下幾點(diǎn)疑惑:
不明白Optional的作用是什么?
不清楚在什么情況下使用Optional?
面對(duì)多層嵌套的情況Optional如何拆解?
Optional和Stream之間的關(guān)系?
通過(guò)系統(tǒng)的學(xué)習(xí)以上幾點(diǎn)疑惑基本都已經(jīng)解決了,在今后的開(kāi)發(fā)中也將開(kāi)始大量使用Optional這個(gè)方法來(lái)幫助自己的代碼提高安全性和可讀性.最后再附上一張思維導(dǎo)圖用來(lái)描述Optional的總體概況
思維導(dǎo)圖
在此我向大家推薦一個(gè)架構(gòu)學(xué)習(xí)交流群。交流學(xué)習(xí)群號(hào):938837867 暗號(hào):555 里面會(huì)分享一些資深架構(gòu)師錄制的視頻錄像:有Spring随常,MyBatis萄涯,Netty源碼分析涝影,高并發(fā)、高性能序目、分布式、微服務(wù)架構(gòu)的原理刻蟹,JVM性能優(yōu)化嘿辟、分布式架構(gòu)等這些成為架構(gòu)師必備