最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java8中Optional的一些常見錯誤用法總結(jié)

 更新時間:2018年07月29日 11:35:13   作者:Yanbin  
我們知道 Java 8 增加了一些很有用的 API, 其中一個就是 Optional,下面這篇文章主要給大家介紹了關(guān)于Java8中Optional的一些常見錯誤用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下

前言

Java 8 引入的 Optional 類型,基本是把它當作 null 值優(yōu)雅的處理方式。其實也不完全如此,Optional 在語義上更能體現(xiàn)有還是沒有值。所以它不是設(shè)計來作為 null 的替代品,如果方法返回 null 值表達了二義性,沒有結(jié)果或是執(zhí)行中出現(xiàn)異常。

在 Oracle  做  Java 語言工作的  Brian Goetz 在 Stack Overflow 回復(fù) Should Java 8 getters return optional type? 中講述了引入  Optional 的主要動機。

Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”, and using null for such was overwhelmingly likely to cause errors.

說的是  Optional 提供了一個有限的機制讓類庫方法返回值清晰的表達有與沒有值,避免很多時候 null 造成的錯誤。并非有了  Optional 就要完全杜絕 NullPointerException。

在 Java 8 之前一個實踐是方法返回集合或數(shù)組時,應(yīng)返回空集合或數(shù)組表示沒有元素; 而對于返回對象,只能用 null 來表示不存在,現(xiàn)在可以用  Optional 來表示這個意義。

自 Java8 于  2014-03-18 發(fā)布后已 5 年有余,這里就列舉幾個我們在項目實踐中使用 Optional 常見的幾個用法。

Optional 類型作為字段或方法參數(shù)

這兒把 Optional  類型用為字段(類或?qū)嵗兞?和方法參數(shù)放在一起來講,是因為假如我們使用 IntelliJ IDEA 來寫 Java 8 代碼,IDEA 對于  Optional 作為字段和方法參數(shù)會給出同樣的代碼建議:

Reports any uses of java.util.Optional<T> , java.util.OptionalDouble , java.util.OptionalInt , java.util.OptionalLong or com.google.common.base.Optional as the type for a field or parameter. Optional was designed to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result". Using a field with type java.util.Optional is also problematic if the class needs to be Serializable , which java.util.Optional is not.

不建議用任何的 Optional 類型作為字段或參數(shù),Optional 設(shè)計為有限的機制讓類庫方法返回值清晰的表達 "沒有值"。 Optional 是不可被序列化的,如果類是可序列化的就會出問題。

上面其實重復(fù)了 Java 8 引入  Optional 的意圖,我們還有必要繼續(xù)深入理解一下為什么不該用  Optional 作為字段或方法參數(shù)。

當我們選擇 Optional 類型而非內(nèi)部包裝的類型后,應(yīng)該是假定了該 Optional 類型不為 null,否則我們在使用 Optional 字段或方法參數(shù)時就變得復(fù)雜了,需要進行兩番檢查。

public class User {
 private String firstName;
 private Optional<String> middleName = Optional.empty();
 private String lastName;
 
 public void setMiddleName(Optional<String> middleName) {
  this.middleName = middleName;
 }
 
 public String getFullName() {
  String fullName = firstName;
  if(middleName != null) {
   if(middleName.isPresent()){
    fullName = fullName.concat("." + middleName.get());
  }
 
  return fullName.concat("." + lastName);
 }
}

由于 middleName 的 setter 方法,我們可能造成 middleName 變?yōu)?null 值,所以在構(gòu)建 fullName 時必須兩重檢查。

并且在調(diào)用 setMiddleName(...) 方法時也有些累贅了

user.setMiddleName(Optional.empty());
user.setMiddleName(Optional.of("abc"));

而如果字段類型非 Optional 類型,而傳入的方法參數(shù)為 Optional 類型,要進行賦值的話

 private String middleName;
 
 public void updateMiddleName(Optional<String> middleName) {
  if(middleName != null) {
   this.middleName = middleName.orElse(null);
  } else {
   this.middleName = null;
  }
 }

前面兩段代碼如果應(yīng)用 Optional.ofNullable(...) 包裹 Optional 來替代 if(middleName != null) 就更復(fù)雜了。

對于本例直接用 String 類型的 middleName  作為字段或方法參數(shù)就行,null 值可以表達沒有 middleName。如果不允許 null 值  middleName, 顯式的進行入口參數(shù)檢查而拒絕該輸入 -- 拋出異常。

利用 Optional 過度檢查方法參數(shù)

這一 Optional 的用法與之前的可能為 null 值的方法參數(shù),不分清紅皂白就用 if...else 檢查,總有一種不安全感,步步驚心,結(jié)果可能事與愿違。

public User getUserById(String userId) {
 if(userId != null) {
  return userDao.findById(userId);
 } else {
  return null;
 }
}

只是到了 Java 8 改成了用 Optional

 return if(Optional.ofNullable(userId)
  .map(id -> userDao.findById(id))
  .orElse(null);

上面兩段代碼其實是同樣的問題,如果輸入的 userId 是 null 值不調(diào)用 findById(...) 方法而直接返回 null 值,這就有兩個問題

userDao.findById(...)
getUserById(userId)

這種情況下立即拋出 NullPointerException 是一個更好的主意,參考下面的代碼

public User getUserById(String userId) { //拋出出 NullPointerException 如果 null userId
 return userDao.findById(Objects.requireNoNull(userId, "Invalid null userId");
}
 
//or
public User getUserById(String userId) { //拋出 IllegalArgumentException 如果 null userId
 Preconditions.checkArgument(userId != null, "Invalid null userId");
 return userDao.findById(userId);
}

即使用了 Optional 的 orElseThrow 拋出異常也不能明確異常造成的原因,比如下面的代碼

public User getUserById(String userId) {
 return Optional.ofNullable(userId)
  .map(id -> userDao.findById(id))
  orElseThrow(() -> 
   new RuntimeException("userId 是 null 或 findById(id) 返回了 null 值"));
}

糾正辦法是認真的審視方法的輸入?yún)?shù),對不符合要求的輸入應(yīng)立即拒絕,防止對下層的壓力與污染,并報告出準確的錯誤信息,以有利于快速定位修復(fù)。

Optional.map(...) 中再次 null 值判斷

假如有這樣的對象導(dǎo)航關(guān)系 user.getOrder().getProduct().getId() , 輸入是一個  user 對象

 String productId = Optional.ofNullable(user)
  .map(User::getOrder)
  .flatMap(order -> Optional.ofNullable(order.getProduct())) //1
  .flatMap(product -> Optional.ofNullable(product.getId())) //2
  .orElse("");

#1 和 #2 中應(yīng)用 flatMap 再次用 Optional.ofNullable() 是因為擔心 order.getProduct() product.getId() 返回了 null 值,所以又用 Optional.ofNullable(...) 包裹了一次。代碼的執(zhí)行結(jié)果仍然是對的,代碼真要這么寫的話真是 Oracle 的責任。這忽略了 Optional.map(...) 的功能,只要看下它的源代碼就知道

 public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
  Objects.requireNonNull(mapper);
  if (!isPresent())
   return empty();
  else {
   return Optional.ofNullable(mapper.apply(value));
  }
 }

map(...) 函數(shù)中已有考慮拆解后的 null 值,因此呢 flatMap 中又 Optional.ofNullable 是多余的,只需簡單一路用 map(...) 函數(shù)

 String productId = Optional.ofNullable(user)
  .map(User::getOrder)
  .map(order -> order.getProduct()) //1
  .map(product -> product.getId()) //2
  .orElse("");

Optional.ofNullable 應(yīng)用于明確的非  null 值

如果有時候只需要對一個明確不為 null 的值進行 Optional 包裝的話,就沒有必要用 ofNullable(...) 方法,例如

public Optional<User> getUserById(String userId) {
 if("ADMIN".equals(userId)) {
  User adminUser = new User("admin");
  return Optional.ofNullable(adminUser); //1
 } else {
  return userDao.findById(userId);
 }
}

在代碼 #1 處非常明確 adminUser 是不可能為 null 值的,所以應(yīng)該直接用 Optional.of(adminUser) 。這也是為什么 Optional 要聲明 of(..) ofNullable(..) 兩個方法??纯此鼈兊脑创a:

 public static <T> Optional<T> of(T value) {
  return new Optional<>(value);
 }
 
 public static <T> Optional<T> ofNullable(T value) {
  return value == null ? empty() : of(value);
 }

知道被包裹的值不可能為 null 時調(diào)用 ofNullable(value) 多了一次多余的 null 值檢查。相應(yīng)的對于非 null 值的字面常量

Optional.ofNullable(100); //這樣不好
Optional.of(100);   //應(yīng)該這么用

小結(jié):

  1. 要理解 Optional 的設(shè)計用意,所以語意上應(yīng)用它來表達有/無結(jié)果,不適于作為類字段與方法參數(shù)
  2. 傾向于方法返回單個對象,用 Optional 類型表示無結(jié)果以避免 null 值的二義性
  3. Optional 進行方法參數(shù)檢查不能掩蓋了錯誤,最好是明確非法的參數(shù)輸入及時拋出輸入異常
  4. 對于最后兩種不正確的用法應(yīng)熟悉 Optional 的源代碼實現(xiàn)就能規(guī)避

鏈接:

Java 8 Optional use cases

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Java多線程CAS操作原理代碼實例解析

    Java多線程CAS操作原理代碼實例解析

    這篇文章主要介紹了Java多線程CAS操作原理代碼實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot整合MyBatis四種常用的分頁方式(詳細總結(jié))

    SpringBoot整合MyBatis四種常用的分頁方式(詳細總結(jié))

    這篇文章詳細給大家總結(jié)了SpringBoot整合MyBatis四種常用的分頁方式,文中通過代碼示例為大家介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • Java時區(qū)轉(zhuǎn)換實例代碼解析

    Java時區(qū)轉(zhuǎn)換實例代碼解析

    這篇文章主要介紹了Java時區(qū)轉(zhuǎn)換實例代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • Mybatis中單雙引號引發(fā)的慘案及解決

    Mybatis中單雙引號引發(fā)的慘案及解決

    這篇文章主要介紹了Mybatis中單雙引號引發(fā)的慘案及解決方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java Web用戶登錄實例代碼

    Java Web用戶登錄實例代碼

    這篇文章主要介紹了Java Web用戶登錄實例代碼的相關(guān)資料,非常不錯具有參考借鑒價值,感興趣的朋友一起看看吧
    2016-05-05
  • IDEA:Git stash 暫存分支修改的實現(xiàn)代碼

    IDEA:Git stash 暫存分支修改的實現(xiàn)代碼

    這篇文章主要介紹了IDEA:Git stash 暫存分支修改的實現(xiàn)代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • 基于Java實現(xiàn)本地APK文件搜索與HTTP下載服務(wù)

    基于Java實現(xiàn)本地APK文件搜索與HTTP下載服務(wù)

    在移動應(yīng)用開發(fā)和測試過程中,經(jīng)常需要從本地服務(wù)器獲取最新的 APK 安裝包,本文將詳細介紹如何使用 Java 構(gòu)建一個能夠搜索本地最新 APK 文件并通過 HTTP 請求提供下載服務(wù)的應(yīng)用
    2025-07-07
  • Java如何實現(xiàn)文件壓縮與上傳FTP

    Java如何實現(xiàn)文件壓縮與上傳FTP

    這篇文章主要介紹了Java如何實現(xiàn)文件壓縮與上傳FTP,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • java鍵盤錄入的方法舉例詳解

    java鍵盤錄入的方法舉例詳解

    這篇文章主要給大家介紹了關(guān)于java鍵盤錄入的相關(guān)資料,我們在寫程序的時候,數(shù)據(jù)值都是固定的,但是實際開發(fā)中,數(shù)據(jù)值肯定是變化的,所以,把數(shù)據(jù)改進為鍵盤錄入,提高程序的靈活性,需要的朋友可以參考下
    2023-10-10
  • Windows10系統(tǒng)下修改jar中的文件并重新打包成jar文件然后運行的操作步驟

    Windows10系統(tǒng)下修改jar中的文件并重新打包成jar文件然后運行的操作步驟

    這篇文章主要介紹了Windows10系統(tǒng)下修改jar中的文件并重新打包成jar文件然后運行的操作步驟,文中通過圖文結(jié)合的形式給大家講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-08-08

最新評論

延庆县| 商水县| 芒康县| 荆州市| 黎平县| 乾安县| 分宜县| 即墨市| 玉环县| 丰镇市| 康保县| 石河子市| 延津县| 股票| 巴林左旗| 张家港市| 永州市| 永靖县| 娄烦县| 五莲县| 大竹县| 黑水县| 临澧县| 林西县| 景洪市| 江都市| 东莞市| 文昌市| 桐梓县| 砀山县| 会宁县| 巴塘县| 齐齐哈尔市| 仲巴县| 嘉荫县| 南江县| 怀柔区| 隆安县| 当雄县| 北辰区| 和龙市|