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

JAVA8如何妙用Optional解決NPE問題詳解

 更新時(shí)間:2018年06月11日 09:33:50   作者:孤獨(dú)煙  
在Java中,null代表一個(gè)不存在的對(duì)象,如果對(duì)它進(jìn)行操作就會(huì)拋出java.lang.NullPointerException異常,下面這篇文章主要給大家介紹了關(guān)于JAVA8如何妙用Optional解決NPE問題的相關(guān)資料,需要的朋友可以參考下

引言

NPE(NullPointerException)是調(diào)試程序最常見的異常。google一下有很多關(guān)于方法到底應(yīng)該返回null還是new一個(gè)空對(duì)象的討論。

在文章的開頭,先說下NPE問題,NPE問題就是,我們?cè)陂_發(fā)中經(jīng)常碰到的NullPointerException.假設(shè)我們有兩個(gè)類,他們的UML類圖如下圖所示


在這種情況下,有如下代碼

user.getAddress().getProvince();

這種寫法,在user為null時(shí),是有可能報(bào)NullPointerException異常的。為了解決這個(gè)問題,于是采用下面的寫法

if(user!=null){
 Address address = user.getAddress();
 if(address!=null){
 String province = address.getProvince();
 }
}

這種寫法是比較丑陋的,為了避免上述丑陋的寫法,讓丑陋的設(shè)計(jì)變得優(yōu)雅。JAVA8提供了Optional類來優(yōu)化這種寫法,接下來的正文部分進(jìn)行詳細(xì)說明

API介紹

先介紹一下API,與其他文章不同的是,本文采取類比的方式來講,同時(shí)結(jié)合源碼。而不像其他文章一樣,一個(gè)個(gè)API羅列出來,讓人找不到重點(diǎn)。

(1)Optional(T value),empty(),of(T value),ofNullable(T value)

這四個(gè)函數(shù)之間具有相關(guān)性,因此放在一組進(jìn)行記憶。

先說明一下,Optional(T value),即構(gòu)造函數(shù),它是private權(quán)限的,不能由外部調(diào)用的。其余三個(gè)函數(shù)是public權(quán)限,供我們所調(diào)用。那么,Optional的本質(zhì),就是內(nèi)部?jī)?chǔ)存了一個(gè)真實(shí)的值,在構(gòu)造的時(shí)候,就直接判斷其值是否為空。好吧,這么說還是比較抽象。直接上Optional(T value)構(gòu)造函數(shù)的源碼,如下圖所示


那么,of(T value)的源碼如下

public static <T> Optional<T> of(T value) {
 return new Optional<>(value);
 }

也就是說of(T value)函數(shù)內(nèi)部調(diào)用了構(gòu)造函數(shù)。根據(jù)構(gòu)造函數(shù)的源碼我們可以得出兩個(gè)結(jié)論:

(1)通過of(T value)函數(shù)所構(gòu)造出的Optional對(duì)象,當(dāng)Value值為空時(shí),依然會(huì)報(bào)NullPointerException。

(2)通過of(T value)函數(shù)所構(gòu)造出的Optional對(duì)象,當(dāng)Value值不為空時(shí),能正常構(gòu)造Optional對(duì)象。

除此之外呢,Optional類內(nèi)部還維護(hù)一個(gè)value為null的對(duì)象,大概就是長(zhǎng)下面這樣的

public final class Optional<T> {
 //省略....
 private static final Optional<?> EMPTY = new Optional<>();
 private Optional() {
 this.value = null;
 }
 //省略...
 public static<T> Optional<T> empty() {
 @SuppressWarnings("unchecked")
 Optional<T> t = (Optional<T>) EMPTY;
 return t;
 }
}

那么,empty()的作用就是返回EMPTY對(duì)象。

好了鋪墊了這么多,可以說ofNullable(T value)的作用了,上源碼

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

好吧,大家應(yīng)該都看得懂什么意思了。相比較of(T value)的區(qū)別就是,當(dāng)value值為null時(shí),of(T value)會(huì)報(bào)NullPointerException異常;ofNullable(T value)不會(huì)throw Exception,ofNullable(T value)直接返回一個(gè)EMPTY對(duì)象。
那是不是意味著,我們?cè)陧?xiàng)目中只用ofNullable函數(shù)而不用of函數(shù)呢?

不是的,一個(gè)東西存在那么自然有存在的價(jià)值。當(dāng)我們?cè)谶\(yùn)行過程中,不想隱藏NullPointerException。而是要立即報(bào)告,這種情況下就用Of函數(shù)。但是不得不承認(rèn),這樣的場(chǎng)景真的很少。博主也僅在寫junit測(cè)試用例中用到過此函數(shù)。

(2)orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier)

這三個(gè)函數(shù)放一組進(jìn)行記憶,都是在構(gòu)造函數(shù)傳入的value值為null時(shí),進(jìn)行調(diào)用的。orElse和orElseGet的用法如下所示,相當(dāng)于value值為null時(shí),給予一個(gè)默認(rèn)值:

@Test
public void test() {
 User user = null;
 user = Optional.ofNullable(user).orElse(createUser());
 user = Optional.ofNullable(user).orElseGet(() -> createUser());
 
}

public User createUser(){
 User user = new User();
 user.setName("zhangsan");
 return user;
}

這兩個(gè)函數(shù)的區(qū)別:當(dāng)user值不為null時(shí),orElse函數(shù)依然會(huì)執(zhí)行createUser()方法,而orElseGet函數(shù)并不會(huì)執(zhí)行createUser()方法,大家可自行測(cè)試。

至于orElseThrow,就是value值為null時(shí),直接拋一個(gè)異常出去,用法如下所示

User user = null;
Optional.ofNullable(user).orElseThrow(()->new Exception("用戶不存在"));

(3) map(Function<? super T, ? extends U> mapper)和flatMap(Function<? super T, Optional<U>> mapper)

這兩個(gè)函數(shù)放在一組記憶,這兩個(gè)函數(shù)做的是轉(zhuǎn)換值的操作。

直接上源碼

 public final class Optional<T> {
 //省略....
  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));
  }
 }
 //省略...
  public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
  Objects.requireNonNull(mapper);
  if (!isPresent())
   return empty();
  else {
   return Objects.requireNonNull(mapper.apply(value));
  }
 }
} 

這兩個(gè)函數(shù),在函數(shù)體上沒什么區(qū)別。唯一區(qū)別的就是入?yún)?,map函數(shù)所接受的入?yún)㈩愋蜑镕unction<? super T, ? extends U>,而flapMap的入?yún)㈩愋蜑镕unction<? super T, Optional<U>>。

在具體用法上,對(duì)于map而言:

如果User結(jié)構(gòu)是下面這樣的

public class User {
 private String name;
 public String getName() {
  return name;
 }
}

這時(shí)候取name的寫法如下所示

String city = Optional.ofNullable(user).map(u-> u.getName()).get();

對(duì)于flatMap而言:

如果User結(jié)構(gòu)是下面這樣的

public class User {
 private String name;
 public Optional<String> getName() {
  return Optional.ofNullable(name);
 }
}

這時(shí)候取name的寫法如下所示

String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();

(4) isPresent()和ifPresent(Consumer<? super T> consumer)

這兩個(gè)函數(shù)放在一起記憶,isPresent即判斷value值是否為空,而ifPresent就是在value值不為空時(shí),做一些操作。這兩個(gè)函數(shù)的源碼如下

public final class Optional<T> {
 //省略....
 public boolean isPresent() {
  return value != null;
 }
 //省略...
 public void ifPresent(Consumer<? super T> consumer) {
  if (value != null)
   consumer.accept(value);
 }
}

需要額外說明的是,大家千萬不要把

if (user != null){
 // TODO: do something
}

給寫成

User user = Optional.ofNullable(user);
if (Optional.isPresent()){
 // TODO: do something
}

因?yàn)檫@樣寫,代碼結(jié)構(gòu)依然丑陋。博主會(huì)在后面給出正確寫法

至于ifPresent(Consumer<? super T> consumer),用法也很簡(jiǎn)單,如下所示

Optional.ofNullable(user).ifPresent(u->{
   // TODO: do something
  });

(5) filter(Predicate<? super T> predicate)

不多說,直接上源碼

public final class Optional<T> {
 //省略....
 Objects.requireNonNull(predicate);
  if (!isPresent())
   return this;
  else
   return predicate.test(value) ? this : empty();
}

filter 方法接受一個(gè) Predicate 來對(duì) Optional 中包含的值進(jìn)行過濾,如果包含的值滿足條件,那么還是返回這個(gè) Optional;否則返回 Optional.empty。

用法如下

Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);

如上所示,如果user的name的長(zhǎng)度是小于6的,則返回。如果是大于6的,則返回一個(gè)EMPTY對(duì)象。

實(shí)戰(zhàn)使用

例一

在函數(shù)方法中

以前寫法

public String getCity(User user) throws Exception{
  if(user!=null){
   if(user.getAddress()!=null){
    Address address = user.getAddress();
    if(address.getCity()!=null){
     return address.getCity();
    }
   }
  }
  throw new Excpetion("取值錯(cuò)誤"); 
 }

JAVA8寫法

public String getCity(User user) throws Exception{
 return Optional.ofNullable(user)
     .map(u-> u.getAddress())
     .map(a->a.getCity())
     .orElseThrow(()->new Exception("取指錯(cuò)誤"));
}

例二

比如,在主程序中

以前寫法

if(user!=null){
 dosomething(user);
}

JAVA8寫法

 Optional.ofNullable(user)
   .ifPresent(u->{
   dosomething(u);
   });

例三

以前寫法

public User getUser(User user) throws Exception{
 if(user!=null){
  String name = user.getName();
  if("zhangsan".equals(name)){
   return user;
  }
 }else{
  user = new User();
  user.setName("zhangsan");
  return user;
 }
}

java8寫法

public User getUser(User user) {
 return Optional.ofNullable(user)
     .filter(u->"zhangsan".equals(u.getName()))
     .orElseGet(()-> {
      User user1 = new User();
      user1.setName("zhangsan");
      return user1;
     });
}

其他的例子,不一一列舉了。不過,博主認(rèn)為,其實(shí)采用這種鏈?zhǔn)骄幊?,雖然代碼優(yōu)雅了。但是,邏輯性沒那么明顯,可讀性有所降低,大家項(xiàng)目中看情況酌情使用。

總結(jié)

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

相關(guān)文章

  • Java中使用數(shù)組實(shí)現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)實(shí)例

    Java中使用數(shù)組實(shí)現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)實(shí)例

    這篇文章主要介紹了Java中使用數(shù)組實(shí)現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)實(shí)例,本文先是講解了實(shí)現(xiàn)棧至少應(yīng)該包括以下幾個(gè)方法等知識(shí),然后給出代碼實(shí)例,需要的朋友可以參考下
    2015-01-01
  • Java 自定義動(dòng)態(tài)數(shù)組方式

    Java 自定義動(dòng)態(tài)數(shù)組方式

    這篇文章主要介紹了Java自定義動(dòng)態(tài)數(shù)組方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • IDEA 打開java文件對(duì)應(yīng)的class路徑的操作步驟

    IDEA 打開java文件對(duì)應(yīng)的class路徑的操作步驟

    這篇文章主要介紹了IDEA 打開java文件對(duì)應(yīng)的class路徑的操作步驟,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • java?stream實(shí)現(xiàn)分組BigDecimal求和以及自定義分組求和

    java?stream實(shí)現(xiàn)分組BigDecimal求和以及自定義分組求和

    這篇文章主要給大家介紹了關(guān)于java?stream實(shí)現(xiàn)分組BigDecimal求和以及自定義分組求和的相關(guān)資料,Stream是Java8的一大亮點(diǎn),是對(duì)容器對(duì)象功能的增強(qiáng),它專注于對(duì)容器對(duì)象進(jìn)行各種非常便利、高效的聚合操作或者大批量數(shù)據(jù)操作,需要的朋友可以參考下
    2023-12-12
  • Spring為何要用三級(jí)緩存來解決循環(huán)依賴問題

    Spring為何要用三級(jí)緩存來解決循環(huán)依賴問題

    這篇文章主要給大家介紹了關(guān)于Spring為何要用三級(jí)緩存來解決循環(huán)依賴問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • MySQL數(shù)據(jù)文件直接通過拷貝備份與恢復(fù)的操作方法

    MySQL數(shù)據(jù)文件直接通過拷貝備份與恢復(fù)的操作方法

    這篇文章主要介紹了MySQL數(shù)據(jù)文件直接通過拷貝備份與恢復(fù)的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Java編程中正則表達(dá)式的用法總結(jié)

    Java編程中正則表達(dá)式的用法總結(jié)

    這篇文章主要介紹了Java編程中正則表達(dá)式的用法總結(jié),正則表達(dá)式是一種強(qiáng)大的字符串處理工具,Java對(duì)正則表達(dá)式的支持還是很不錯(cuò)的,我們先來整理一下正則表達(dá)式的一些基礎(chǔ)知識(shí):
    2016-05-05
  • Java代碼優(yōu)化細(xì)節(jié)

    Java代碼優(yōu)化細(xì)節(jié)

    這篇文章主要為大家詳細(xì)介紹了Java代碼優(yōu)化細(xì)節(jié),通過不同細(xì)節(jié)對(duì)java代碼進(jìn)行優(yōu)化,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Java的異常處理體系詳解

    Java的異常處理體系詳解

    這篇文章主要介紹了Java的異常處理體系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Spring boot啟動(dòng)流程之解決循環(huán)依賴的方法

    Spring boot啟動(dòng)流程之解決循環(huán)依賴的方法

    循環(huán)依賴,指的是兩個(gè)bean之間相互依賴,形成了一個(gè)循環(huán),spring解決循環(huán)依賴的方式是在bean的實(shí)例化完成之后,所以不要在構(gòu)造方法中引入循環(huán)依賴,因?yàn)檫@時(shí)對(duì)象還沒有實(shí)例化,spring也無法解決,本文給大家介紹Spring boot循環(huán)依賴的解決方法,一起看看吧
    2024-02-02

最新評(píng)論

锦州市| 牟定县| 砚山县| 彰化县| 牙克石市| 宣恩县| 卫辉市| 清镇市| 徐州市| 禄丰县| 专栏| 商水县| 翁牛特旗| 略阳县| 鹤岗市| 廉江市| 蕲春县| 黑龙江省| 黑龙江省| 扶绥县| 安多县| 马鞍山市| 云梦县| 铁岭市| 曲麻莱县| 湟源县| 宾川县| 珠海市| 鄂托克前旗| 南和县| 宁陕县| 安义县| 博爱县| 资阳市| 房产| 丰都县| 玛纳斯县| 林州市| 佛坪县| 商水县| 托里县|