Java Optional實踐(小結(jié))
問題描述
在大熱的Spring Boot 2.0中,在將原來的泛型改為了Optional,旨在讓我們的代碼更簡潔。

實踐
Optional
很簡單的一個類,點開它的源代碼,其中所有的方法都是與null相關(guān)聯(lián)的。

這是一個簡化我們處理null的類。
它就是一個容器,其中有我們想要的對象,但是該對象有時候會是空,所以我們需要使用Optional封裝好的方法來獲取需要的對象。從而很好地避免了空指針異常。

錯誤示范
我看到網(wǎng)上很多人這么寫:
catRepository.findById(id).get();
下面是Spring Boot 1.5的寫法,那請問:如果上面的寫法是正確的,那為什么還要大費周章設(shè)計一個Optional呢?
catRepository.findOne(id);
分析
通過get是能獲取到我們需要的對象。
但是看看get的源代碼,這樣寫,拋出了NoSuchElementException異常,這個異常我們沒法在全局中處理它。
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
為什么不能再全局中處理呢?大家可以思考一下:
因為NoSuchElementException覆蓋的范圍太廣了,只要是Optional中有null就會拋出NoSuchElementException,很多情況下都會造成這種異常,那我們究竟要給用戶一個什么樣的提示信息好呢?最后還是給出500服務(wù)器異常,那異常處理的意義何在呢?
所以我們需要用Optional來拋出一個有特定范圍的能被全局準(zhǔn)確處理的異常。
Cat cat = catRepository.findOne(id);
if (null == cat) {
throw new EntityNotFoundException("該實體找不到");
}
return cat;
思想都是一樣,我們不過是用一種更簡潔的寫法實現(xiàn)上面的功能。
實現(xiàn)
沒錯,就像下面一樣,我們只需要一行代碼!
public Cat findById(Long id) {
return catRepository.findById(id).orElseThrow(EntityNotFoundException::new);
}
findById返回一個Optional,然后調(diào)用該對象的orElseThrow方法。

orElseThrow方法,如果存在,返回包含的值,否則拋出異常。
該方法的參數(shù)是一個lamda表達(dá)式。這里就不深究lamda表達(dá)式的幾種類型了,如果感興趣可以自行研究下Function、Consumer、Predicate、Supplier這四個函數(shù)式接口的區(qū)別。

所以傳一個lamda表達(dá)式進去,然后IDEA會給出警告:

Can be replaced with method reference
該lamda表達(dá)式能被一個方法引用代替,Alt + Enter,我們最終的代碼就長這樣:

這里的::是lamda表達(dá)式的一種簡寫,是Java8中的新特性,看著可能有點奇怪,原來,編譯器比程序員聰明多了。
異常處理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<String> entityNotFoundHandler() {
return new ResponseEntity<>("您要找的實體不存在", HttpStatus.NOT_FOUND);
}
}
寫個控制器增強,全局處理異常,這里的RestControllerAdvice又是一個組合注解:

處理異常,同時以Json的格式返回。
@Test
public void findById() throws Exception {
this.mockMvc.perform(get("/cat/1"))
.andDo(print());
}
寫個控制器的單元測試,查詢一個不存在的實體,運行,看控制臺的打印輸出:

一勞永逸
一勞永逸,這是我們最喜歡的東西了。
return catRepository.findById(id).orElseThrow(EntityNotFoundException::new);
以后再查詢,就這一行,再也不用去判斷null了。
NotNull
正所謂條條大路通羅馬,對null的一勞永逸,我們這樣實現(xiàn),別人也可以那樣實現(xiàn)。
如果你在Spring的項目中打過斷點調(diào)試的話,那我斷定你一定見過下面這行代碼:
Assert.notNull();
以下是該方法的源碼,注意這里的Assert是org.springframework.util包下的:

剛方法用于判斷null,如果為空,則拋出異常。
隨便點開一個方法,都會在第一行為不該為null的參數(shù)進行判斷。

這里,不禁對整個框架肅然起敬,同樣一個方法,大牛寫了二十分鐘,而你寫了十分鐘,但是你卻去改了半個小時的bug。
@Nullable
可能在上面看到了我們不熟悉的注解@Nullable,表示從來沒見過,這個注解干什么用的呢?
萬能的StackOverflow又給出了完美的回答:

這會讓你的代碼更清晰,如果你重寫這個方法,你也需要讓參數(shù)可為空。通常也用于代碼提示。
@Nullable和@NotNull這一對注解,沒什么實際意義,只是用于代碼更清晰,同時編譯器能給出我們提示。
總結(jié)
之前一直抱怨Java更新的太快,學(xué)校教的是Java5之前的東西,從Java5開始有的注解,但是從來沒講過這個東西,然而去看看官方的描述:

其實,Java的每次更新,都是為了我們更簡潔優(yōu)雅的代碼而努力。去看看官方的描述,Java讓我們將更多的精力放在think上,而不是code上。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
如何在springMVC的controller中獲取request
這篇文章主要介紹了如何在springMVC的controller中獲取request,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12
Java中FileOutputStream流的write方法
這篇文章主要為大家詳細(xì)介紹了Java中FileOutputStream流的write方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08
SpringMVC的組件之HandlerExceptionResolver詳解
這篇文章主要介紹了SpringMVC的組件之HandlerExceptionResolver詳解,不管是在處理請求映射(HandlerMapping),還是在請求被處理(Handler)時拋出的異常,DispatcherServlet都會委托給HandlerExceptionResolver進行異常處理,該接口只有一個方法,需要的朋友可以參考下2023-10-10
SpringBoot 啟動報錯Unable to connect to 
這篇文章主要介紹了SpringBoot 啟動報錯Unable to connect to Redis server: 127.0.0.1/127.0.0.1:6379問題的解決方案,文中通過圖文結(jié)合的方式給大家講解的非常詳細(xì),對大家解決問題有一定的幫助,需要的朋友可以參考下2024-10-10
使用FeignClient調(diào)用遠(yuǎn)程服務(wù)時整合本地的實現(xiàn)方法
這篇文章主要介紹了使用FeignClient調(diào)用遠(yuǎn)程服務(wù)時整合本地的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03

