Java8使用 Optional 处理 null
轉(zhuǎn)載自??Java8(5):使用 Optional 處理 null
寫過 Java 程序的同學(xué),一般都遇到過?NullPointerException?:) —— 為了不拋出這個異常,我們便會寫如下的代碼:
User user = getUserById(id); if (user != null) {String username = user.getUsername();System.out.println("Username is: " + username); // 使用 username }但是很多時候,我們可能會忘記寫?if (user != null)?—— 如果在開發(fā)階段就發(fā)現(xiàn)那還好,但是如果在開發(fā)階段沒有測試到問題,等到上線卻出了?NullPointerException?... 畫面太美,我不敢繼續(xù)想下去。
為了解決這種尷尬的處境,JDK 終于在 Java8 的時候加入了?Optional?類。Optional?的 javadoc 介紹:
A container object which may or may not contain a non-null value. If a value is present,?isPresent()?will return?true?and?get()?will return the value.這是一個可以包含或者不包含非?null?值的容器。如果值存在則?isPresent()方法會返回?true,調(diào)用?get()?方法會返回該對象。
?
JDK 提供三個靜態(tài)方法來構(gòu)造一個?Optional:
1.Optional.of(T value),該方法通過一個非?null?的?value?來構(gòu)造一個?Optional,返回的?Optional?包含了?value?這個值。對于該方法,傳入的參數(shù)一定不能為?null,否則便會拋出?NullPointerException。
2.Optional.ofNullable(T value),該方法和?of?方法的區(qū)別在于,傳入的參數(shù)可以為?null?—— 但是前面 javadoc 不是說?Optional?只能包含非?null?值嗎?我們可以看看?ofNullable?方法的源碼:
?
原來該方法會判斷傳入的參數(shù)是否為?null,如果為?null?的話,返回的就是?Optional.empty()。
3.Optional.empty(),該方法用來構(gòu)造一個空的?Optional,即該?Optional?中不包含值 —— 其實底層實現(xiàn)還是?如果Optional?中的?value?為?null?則該?Optional?為不包含值的狀態(tài),然后在 API 層面將?Optional?表現(xiàn)的不能包含?null值,使得?Optional?只存在?包含值?和?不包含值?兩種狀態(tài)。
?
前面 javadoc 也有提到,Optional?的?isPresent()?方法用來判斷是否包含值,get()?用來獲取?Optional?包含的值 —— 值得注意的是,如果值不存在,即在一個Optional.empty?上調(diào)用?get()?方法的話,將會拋出?NoSuchElementException?異常。
我們假設(shè)?getUserById?已經(jīng)是個客觀存在的不能改變的方法,那么利用?isPresent?和?get?兩個方法,我們現(xiàn)在能寫出下面的代碼:
好像看著代碼是優(yōu)美了點 —— 但是事實上這與之前判斷?null?值的代碼沒有本質(zhì)的區(qū)別,反而用?Optional?去封裝?value,增加了代碼量。所以我們來看看?Optional?還提供了哪些方法,讓我們更好的(以正確的姿勢)使用?Optional。
1.ifPresent
?
如果?Optional?中有值,則對該值調(diào)用?consumer.accept,否則什么也不做。
所以對于上面的例子,我們可以修改為:
2.orElse
?
如果?Optional?中有值則將其返回,否則返回?orElse?方法傳入的參數(shù)。
User user = Optional.ofNullable(getUserById(id)).orElse(new User(0, "Unknown"));System.out.println("Username is: " + user.getUsername());3.orElseGet
?
orElseGet?與?orElse?方法的區(qū)別在于,orElseGet?方法傳入的參數(shù)為一個?Supplier?接口的實現(xiàn) —— 當(dāng)?Optional中有值的時候,返回值;當(dāng)?Optional?中沒有值的時候,返回從該?Supplier?獲得的值。
User user = Optional.ofNullable(getUserById(id)).orElseGet(() -> new User(0, "Unknown"));System.out.println("Username is: " + user.getUsername());4.orElseThrow
?
orElseThrow?與?orElse?方法的區(qū)別在于,orElseThrow?方法當(dāng)?Optional?中有值的時候,返回值;沒有值的時候會拋出異常,拋出的異常由傳入的?exceptionSupplier?提供。
User user = Optional.ofNullable(getUserById(id)).orElseThrow(() -> new EntityNotFoundException("id 為 " + id + " 的用戶沒有找到"));舉一個?orElseThrow?的用途:在 SpringMVC 的控制器中,我們可以配置統(tǒng)一處理各種異常。查詢某個實體時,如果數(shù)據(jù)庫中有對應(yīng)的記錄便返回該記錄,否則就可以拋出?EntityNotFoundException?,處理?EntityNotFoundException?的方法中我們就給客戶端返回Http 狀態(tài)碼 404 和異常對應(yīng)的信息 ——?orElseThrow?完美的適用于這種場景。
@RequestMapping("/{id}") public User getUser(@PathVariable Integer id) {Optional<User> user = userService.getUserById(id);return user.orElseThrow(() -> new EntityNotFoundException("id 為 " + id + " 的用戶不存在")); }@ExceptionHandler(EntityNotFoundException.class) public ResponseEntity<String> handleException(EntityNotFoundException ex) {return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND); }5.map
?
如果當(dāng)前?Optional?為?Optional.empty,則依舊返回?Optional.empty;否則返回一個新的?Optional,該?Optional包含的是:函數(shù)?mapper?在以?value?作為輸入時的輸出值。
Optional<String> username = Optional.ofNullable(getUserById(id)).map(user -> user.getUsername());System.out.println("Username is: " + username.orElse("Unknown"));而且我們可以多次使用?map?操作:
Optional<String> username = Optional.ofNullable(getUserById(id)).map(user -> user.getUsername()).map(name -> name.toLowerCase()).map(name -> name.replace('_', ' '));System.out.println("Username is: " + username.orElse("Unknown"));6.flatMap
?
flatMap?方法與?map?方法的區(qū)別在于,map?方法參數(shù)中的函數(shù)?mapper?輸出的是值,然后?map?方法會使用?Optional.ofNullable?將其包裝為?Optional;而?flatMap?要求參數(shù)中的函數(shù)?mapper?輸出的就是?Optional。
Optional<String> username = Optional.ofNullable(getUserById(id)).flatMap(user -> Optional.of(user.getUsername())).flatMap(name -> Optional.of(name.toLowerCase()));System.out.println("Username is: " + username.orElse("Unknown"));7.filter
?
filter?方法接受一個?Predicate?來對?Optional?中包含的值進行過濾,如果包含的值滿足條件,那么還是返回這個?Optional;否則返回?Optional.empty。
Optional<String> username = Optional.ofNullable(getUserById(id)).filter(user -> user.getId() < 10).map(user -> user.getUsername());System.out.println("Username is: " + username.orElse("Unknown"));有了?Optional,我們便可以方便且優(yōu)雅的在自己的代碼中處理?null?值,而不再需要一昧通過容易忘記和麻煩的?if (object != null)?來判斷值不為?null。如果你的程序還在使用 Java8 之前的 JDK,可以考慮引入 Google 的 Guava 庫 —— 事實上,早在 Java6 的年代,Guava 就提供了?Optional?的實現(xiàn)。
號外:Java9 對?Optional?的增強
即將在今年 7 月到來的 JDK9 中,在?Optional?類中添加了三個新的方法:
or?方法的作用是,如果一個?Optional?包含值,則返回自己;否則返回由參數(shù)?supplier?獲得的?Optional
ifPresentOrElse?方法的用途是,如果一個?Optional?包含值,則對其包含的值調(diào)用函數(shù)?action,即?action.accept(value),這與?ifPresent?一致;與?ifPresent?方法的區(qū)別在于,ifPresentOrElse?還有第二個參數(shù)?emptyAction?—— 如果?Optional?不包含值,那么?ifPresentOrElse?便會調(diào)用?emptyAction,即?emptyAction.run()
stream?方法的作用就是將?Optional?轉(zhuǎn)為一個?Stream,如果該?Optional?中包含值,那么就返回包含這個值的?Stream;否則返回一個空的?Stream(Stream.empty())。
舉個例子,在 Java8,我們會寫下面的代碼:
而有了?Optional.stream(),我們就可以將其簡化為:
public List<User> getUsers(Collection<Integer> userIds) {return userIds.stream().map(this::getUserById) // 獲得 Stream<Optional<User>>.flatMap(Optional::stream) // Stream 的 flatMap 方法將多個流合成一個流.collect(Collectors.toList()); }總結(jié)
以上是生活随笔為你收集整理的Java8使用 Optional 处理 null的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis 如何分析慢查询操作
- 下一篇: b站怎么上传视频