Optional的學習與實戰
整片文章大部分內容來自java8實戰這本書,我在這裏也是將自己的學習過程記錄下來,並且整理成筆記給需要的人提供一個方便,在學習的過程中主要有以下幾點疑惑:
- 不明白Optional的作用是什麼?
- 不清楚在什麼情況下使用Optional?
- 面對多層嵌套的情況Optional如何拆解?
- Optional和Stream之間的關係?
寫在前面
作爲一個java開發程序猿如果說你沒有遇到過空指針的問題,那真的是太神奇了,在我們的工作中,常常會想盡一切辦法避免空指針的.事實上也有人曾經提出空引用的想法,就是對null依然進行引用,但是這種做法所帶來的可怕影響就是會在後期帶啦大量的NullPrinterException異常.
本文的目的是使用Optional來取代null,並且使用Optional來去除代碼中大量的null檢查從而提高我們程序的可讀性和代碼的健壯性.
Null帶來的種種問題
- 錯誤之源:NullPointExpection是java開發中最經常遭遇的異常.
- 是你的代碼膨脹:頁面中充斥着過多而null檢查,是代碼的可讀性大大降低.
- 自身毫無意義:自身沒有任何意義,這本身就是一種錯誤的建模.
- 破壞了java的哲學性:java一直試圖讓人們避免接觸指針的問題,但是null是唯一的例外
- 在java類型系統上開了一個口子:null並不屬於任何類型,這意味着他可以賦值給任意變量.
其他語言中null的替代品
在Groovy中可以通過安全導航操作符(Safe Navigation Operator,標記爲?)
carInsuranceName= person?.car?.insurance?.name
在使用變量是可能出現null的情況,但是在這種情況下不會拋出空指針的異常,而是將null沿着調用鏈一直傳遞下去.最終返回一個null.
在java8中引入了Optional的概念設置了一個新的類java.util.Option的類型,從而對可能出現null的對象進行建模
Optional類入門
如果我們需要使用一個對象,這個對象中可能存在null,我們可以聲明其爲Optional類型,變量存在是返回的是封裝的對象,當變量不存在使返回的是一個空的Optional對象.在語法上你可以把這個當做一回事,但是實際中他們之間存才非常大的差別.如果你引用的是一個null,一定會觸發NullPointException
,如果使用Optional.empty()就完全沒有事.
**使用Optional而不是null是一個非常重要而又實際的語義區別.
使用Optional對你的代碼進行重構.
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;
}
創建Optioanl對象
Optional提供了集中靜態方法供我們直接使用
聲明一個空的Optional
直接調用靜態工廠的方法Optional.empty創建一個對象
Optional<EssayQuestionGroupResponse> empty = Optional.empty();
根據非空值創建Optional
直接調用講臺方法Optional.of,依據一個非空值創建一個Optional對象
Optional<EssayQuestionGroupResponse> essayQuestionGroupResponse = Optional.of(new EssayQuestionGroupResponse());
可接受null的Optional
使用講臺工廠方法Optional.ofNullable創建一個允許null值得Optional對象
EssayQuestionGroupResponse essayQuestionGroupResponseNull = new EssayQuestionGroupResponse();
Optional<EssayQuestionGroupResponse> essayQuestionGroupResponseNullOptional = Optional.ofNullable(essayQuestionGroupResponseNull);
獲取Optional中的對象
我們將對象放到了Optional中,他們本身提供了一個get方法,但是如果我們不按照約定進行get那麼他依然會拋出一個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對象中提取和轉化值
從對象中提取信息這是很常見的模式,Optional提供了一個map方法,他的工作方式如下.
essayQuestionGroupResponse.map(EssayQuestionGroupResponse::getGroupId);
同學們也很容易發現,如果想要獲取嵌套對象,讓然使用map是不可取的,這裏需要使用flatMap
essayQuestionGroupResponse
.flatMap(EssayQuestionGroupResponse::getMaterialList)
.map(EssayQuestionGroupResponse.Material::getMaterialContent).orElse("null"))
在最後使用orElse如果在調用鏈中的認識一環出現空指針則會返回orElse中的內容
Optional<EssayQuestionGroupResponse> essayQuestionGroupResponse = Optional.of(new EssayQuestionGroupResponse());
//這裏會出現空值,以後在研究
System.out.println(essayQuestionGroupResponse
.flatMap(EssayQuestionGroupResponse::getMaterialList)
.map(Material::getMaterialContent).orElse("null"));
}
默認行爲以及街引用Optional對象
再生產中有如下幾種方式獲取到Optional中的對象
- get()是最簡單的方法,並且不安全,如果變量存在直接返回封裝的變量值,否做就跑出一個NoSuchElementException異常 .
- orElse(T other)當Optional中不包含值得時候提供一個默認值.
- orElseGet(Supplier<? extends T>)是上一個方法的延時調用版,需要提高性能可以使用此方法.
- orElseThrow(Supplier<? extends exceptionSupplier>)如果對象爲空的時候回拋出一個異常.
- ifPresent(Consumer<? super T>) 讓變量在存在的時候作爲一個消費者傳入一個方法否則不執行任何操作.
實戰實例
有效的使用Optional類意味着在需要處理潛在缺失值得時候可以進行全面的反思了.
用Optional封裝可能爲null的值
現存的很多方法都是通過返回一個null來表示沒有這個值,就好比我們經常使用的map.get()方法,如果該map中不存在對應屬性則會返回一個null.這是用Optional將代碼封裝起來就可以減少if-else的判斷.
Optional<String> value=Optional.ofNullable(map.get("key"));
每次在獲取潛爲null的對象時都可以調用這個方法.
異常與Optional的對比
由於某種原因,函數無法返回某個值,這是除了返回null,JavaAPI通常的做法是拋出一個異常.這種情況比較典型的例子就是Integer.parseInt(String),如果轉化失敗則會拋出一個NumberFormatException,這種情況和之前的null相比唯一不同的處理方法就是使用try/catch,而之前是使用if/else進行判斷.這時候我們可以用如下的方式完成這個功能.
public static Optional<Integer> stringToInt (String s) {
try {
return Optional.of(Integer.parseInt(s);
} catch (NumberFormatException e) {
return Optional.empty();
}
}
我們可以將多中類似的方法封裝到一個工具類中進行使用.
總結
整片文章大部分內容來自java8實戰這本書,我在這裏也是將自己的學習過程記錄下來,並且整理成筆記給需要的人提供一個方便,在學習的過程中主要有以下幾點疑惑:
- 不明白Optional的作用是什麼?
- 不清楚在什麼情況下使用Optional?
- 面對多層嵌套的情況Optional如何拆解?
- Optional和Stream之間的關係?
通過系統的學習以上幾點疑惑基本都已經解決了,在今後的開發中也將開始大量使用Optional這個方法來幫助自己的代碼提高安全性和可讀性.最後再附上一張思維導圖用來描述Optional的總體概況