欧美成人午夜免费全部完,亚洲午夜福利精品久久,а√最新版在线天堂,另类亚洲综合区图片小说区,亚洲欧美日韩精品色xxx

扣丁學(xué)堂盤點(diǎn)Java8中Optional的一些常見錯(cuò)誤用法總結(jié)

2018-07-30 15:01:53 1859瀏覽

今天扣丁學(xué)堂Java培訓(xùn)老師給大家介紹一下關(guān)于Java8中Optional的一些常見錯(cuò)誤用法匯總,首先Java8引入的Optional類型,基本是把它當(dāng)作null值優(yōu)雅的處理方式。其實(shí)也不完全如此,Optional在語義上更能體現(xiàn)有還是沒有值。所以它不是設(shè)計(jì)來作為null的替代品,如果方法返回null值表達(dá)了二義性,沒有結(jié)果或是執(zhí)行中出現(xiàn)異常。

在Oracle做Java語言工作的BrianGoetz在StackOverflow回復(fù)ShouldJava8gettersreturnoptionaltype?中講述了引入Optional的主要?jiǎng)訖C(jī)。

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提供了一個(gè)有限的機(jī)制讓類庫(kù)方法返回值清晰的表達(dá)有與沒有值,避免很多時(shí)候null造成的錯(cuò)誤。并非有了Optional就要完全杜絕NullPointerException。

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

自Java8于2014-03-18發(fā)布后已5年有余,這里就列舉幾個(gè)我們?cè)陧?xiàng)目實(shí)踐中使用Optional常見的幾個(gè)用法。

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

這兒把Optional類型用為字段(類或?qū)嵗兞?和方法參數(shù)放在一起來講,是因?yàn)榧偃缥覀兪褂肐ntelliJIDEA來寫Java8代碼,IDEA對(duì)于Optional作為字段和方法參數(shù)會(huì)給出同樣的代碼建議:
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è)計(jì)為有限的機(jī)制讓類庫(kù)方法返回值清晰的表達(dá)"沒有值"。Optional是不可被序列化的,如果類是可序列化的就會(huì)出問題。

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

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

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)閚ull值,所以在構(gòu)建fullName時(shí)必須兩重檢查。

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

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

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

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ù)雜了。

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

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

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

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

只是到了Java8改成了用Optional
return if(Optional.ofNullable(userId)
 .map(id -> userDao.findById(id))
 .orElse(null);

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

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

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

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 值"));
}

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

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

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

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()是因?yàn)閾?dān)心order.getProduct()或product.getId()返回了null值,所以又用Optional.ofNullable(...)包裹了一次。代碼的執(zhí)行結(jié)果仍然是對(duì)的,代碼真要這么寫的話真是Oracle的責(zé)任。這忽略了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是多余的,只需簡(jiǎn)單一路用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值

如果有時(shí)候只需要對(duì)一個(gè)明確不為null的值進(jìn)行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(..)兩個(gè)方法??纯此鼈兊脑创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時(shí)調(diào)用ofNullable(value)多了一次多余的null值檢查。相應(yīng)的對(duì)于非null值的字面常量

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

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

扣丁學(xué)堂微信公眾號(hào)



【關(guān)注微信公眾號(hào)獲取更多學(xué)習(xí)資料】



查看更多關(guān)于“Java開發(fā)資訊”的相關(guān)文章>>

標(biāo)簽: Java培訓(xùn) Java開發(fā)程序員 Java視頻教程

熱門專區(qū)

暫無熱門資訊

課程推薦

微信
微博
15311698296

全國(guó)免費(fèi)咨詢熱線

郵箱:codingke@1000phone.com

官方群:148715490

北京千鋒互聯(lián)科技有限公司版權(quán)所有   北京市海淀區(qū)寶盛北里西區(qū)28號(hào)中關(guān)村智誠(chéng)科創(chuàng)大廈4層
京ICP備2021002079號(hào)-2   Copyright ? 2017 - 2022
返回頂部 返回頂部