0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

JAVA8提供了Optional類來優(yōu)化這種寫法

Linux愛好者 ? 來源:CSDN ? 作者:zjhred ? 2022-04-24 15:18 ? 次閱讀

引言

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

9b4c56a8-c39e-11ec-bce3-dac502259ad0.png

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

user.getAddress().getProvince();

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

if(user!=null){
Addressaddress=user.getAddress();
if(address!=null){
Stringprovince=address.getProvince();
}
}

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

一個(gè)連載多年還在繼續(xù)更新的免費(fèi)教程:http://blog.didispace.com/spring-boot-learning-2x/

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ù)的源碼,如下圖所示

9b5ee322-c39e-11ec-bce3-dac502259ad0.png

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

publicstaticOptionalof(Tvalue){
returnnewOptional<>(value);
}

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

  • 通過of(T value)函數(shù)所構(gòu)造出的Optional對(duì)象,當(dāng)Value值為空時(shí),依然會(huì)報(bào)NullPointerException。
  • 通過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)下面這樣的

publicfinalclassOptional<T>{
//省略....
privatestaticfinalOptionalEMPTY=newOptional<>();
privateOptional(){
this.value=null;
}
//省略...
publicstaticOptionalempty(){
@SuppressWarnings("unchecked")
Optionalt=(Optional)EMPTY;
returnt;
}
}

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

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

publicstaticOptionalofNullable(Tvalue){
returnvalue==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 other)和orElseThrow(Supplier exceptionSupplier)

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

@Test
publicvoidtest(){
Useruser=null;
user=Optional.ofNullable(user).orElse(createUser());
user=Optional.ofNullable(user).orElseGet(()->createUser());

}
publicUsercreateUser(){
Useruser=newUser();
user.setName("zhangsan");
returnuser;
}

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

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

Useruser=null;
Optional.ofNullable(user).orElseThrow(()->newException("用戶不存在"));

3、map(Function mapper)和flatMap(Function> mapper)

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

直接上源碼

publicfinalclassOptional<T>{
//省略....
publicOptionalmap(FunctionsuperT,?extendsU>mapper){
Objects.requireNonNull(mapper);
if(!isPresent())
returnempty();
else{
returnOptional.ofNullable(mapper.apply(value));
}
}
//省略...
publicOptionalflatMap(FunctionsuperT,Optional>mapper){
Objects.requireNonNull(mapper);
if(!isPresent())
returnempty();
else{
returnObjects.requireNonNull(mapper.apply(value));
}
}
}

這兩個(gè)函數(shù),在函數(shù)體上沒什么區(qū)別。唯一區(qū)別的就是入?yún)?,map函數(shù)所接受的入?yún)㈩愋蜑?code style="font-size:14px;padding:2px 4px;margin-right:2px;margin-left:2px;color:rgb(30,107,184);background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;">Function,而flapMap的入?yún)㈩愋蜑?code style="font-size:14px;padding:2px 4px;margin-right:2px;margin-left:2px;color:rgb(30,107,184);background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;">Function>。

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

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

publicclassUser{
privateStringname;
publicStringgetName(){
returnname;
}
}

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

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

對(duì)于flatMap而言:

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

publicclassUser{
privateStringname;
publicOptionalgetName(){
returnOptional.ofNullable(name);
}
}

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

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

4、isPresent()和ifPresent(Consumer consumer)

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

publicfinalclassOptional<T>{
//省略....
publicbooleanisPresent(){
returnvalue!=null;
}
//省略...
publicvoidifPresent(ConsumersuperT>consumer){
if(value!=null)
consumer.accept(value);
}
}

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

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

給寫成

Useruser=Optional.ofNullable(user);
if(Optional.isPresent()){
//TODO:dosomething
}

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

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

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

5、filter(Predicate predicate)

不多說,直接上源碼

publicfinalclassOptional<T>{
//省略....
Objects.requireNonNull(predicate);
if(!isPresent())
returnthis;
else
returnpredicate.test(value)?this:empty();
}

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

用法如下

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

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

一個(gè)連載多年還在繼續(xù)更新的免費(fèi)教程:http://blog.didispace.com/spring-boot-learning-2x/

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

例一

在函數(shù)方法中

以前寫法

publicStringgetCity(Useruser)throwsException{
if(user!=null){
if(user.getAddress()!=null){
Addressaddress=user.getAddress();
if(address.getCity()!=null){
returnaddress.getCity();
}
}
}
thrownewExcpetion("取值錯(cuò)誤");
}

JAVA8寫法

publicStringgetCity(Useruser)throwsException{
returnOptional.ofNullable(user)
.map(u->u.getAddress())
.map(a->a.getCity())
.orElseThrow(()->newException("取指錯(cuò)誤"));
}

例二

比如,在主程序中

以前寫法

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

JAVA8寫法

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

例三

以前寫法

publicUsergetUser(Useruser)throwsException{
if(user!=null){
Stringname=user.getName();
if("zhangsan".equals(name)){
returnuser;
}
}else{
user=newUser();
user.setName("zhangsan");
returnuser;
}
}

java8寫法

publicUsergetUser(Useruser){
returnOptional.ofNullable(user)
.filter(u->"zhangsan".equals(u.getName()))
.orElseGet(()->{
Useruser1=newUser();
user1.setName("zhangsan");
returnuser1;
});
}

其他的例子,不一一列舉了。不過采用這種鏈?zhǔn)?a href="http://wenjunhu.com/v/tag/1315/" target="_blank">編程,雖然代碼優(yōu)雅了。但是,邏輯性沒那么明顯,可讀性有所降低,大家項(xiàng)目中看情況酌情使用。

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2970

    瀏覽量

    104814
  • API
    API
    +關(guān)注

    關(guān)注

    2

    文章

    1502

    瀏覽量

    62108

原文標(biāo)題:Java8 判空新寫法!

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Java集合API的改進(jìn)介紹

    解答這些問題。 我們將逐步學(xué)習(xí) Java 集合優(yōu)化過程,并按版本逐一對(duì)比分析。主要討論的焦點(diǎn)將包括 JDK 1.0、1.2、1.4、1.5、1.6、1.8、9、10、11 和 21 版本的
    的頭像 發(fā)表于 11-22 11:12 ?213次閱讀
    <b class='flag-5'>Java</b>集合API的改進(jìn)介紹

    Java 枚舉與策略模式、函數(shù)式接口的結(jié)合:實(shí)現(xiàn)高內(nèi)聚低耦合的設(shè)計(jì)

    作者:京東物流 楊唯一 一、Java 枚舉 Java 枚舉是一個(gè)特殊的,一般表示一組常量,比如一年的 4 個(gè)季節(jié),一年的 12 個(gè)月份,一個(gè)星期的 7 天,方向有東南西北等。 我們
    的頭像 發(fā)表于 11-21 14:06 ?225次閱讀

    使用Arthas火焰圖工具的Java應(yīng)用性能分析和優(yōu)化經(jīng)驗(yàn)

    分享作者在使用Arthas火焰圖工具進(jìn)行Java應(yīng)用性能分析和優(yōu)化的經(jīng)驗(yàn)。
    的頭像 發(fā)表于 10-28 09:27 ?284次閱讀
    使用Arthas火焰圖工具的<b class='flag-5'>Java</b>應(yīng)用性能分析和<b class='flag-5'>優(yōu)化</b>經(jīng)驗(yàn)

    優(yōu)化汽車48V電池和電動(dòng)自行車應(yīng)用中的線路電壓和負(fù)載調(diào)節(jié)

    電子發(fā)燒友網(wǎng)站提供優(yōu)化汽車48V電池和電動(dòng)自行車應(yīng)用中的線路電壓和負(fù)載調(diào)節(jié).pdf》資料免費(fèi)下載
    發(fā)表于 09-06 09:45 ?2次下載
    <b class='flag-5'>優(yōu)化</b>汽車<b class='flag-5'>類</b>48V電池和電動(dòng)自行車應(yīng)用中的線路電壓和負(fù)載調(diào)節(jié)

    一款3605電源芯片的性能優(yōu)化與改進(jìn)思路

    在電源設(shè)計(jì)中,確保電源轉(zhuǎn)換器在各種工作條件下都能提供穩(wěn)定和高效的性能至關(guān)重要。 ASP3605是一款高效同步降壓轉(zhuǎn)換器,它提供多種調(diào)節(jié)選項(xiàng),以優(yōu)化電源的性能。特別是,ITH(Erro
    發(fā)表于 08-23 14:34

    java子類可以繼承父的什么

    Java是一種面向?qū)ο蟮木幊陶Z言,它支持繼承的概念。繼承是面向?qū)ο缶幊痰娜筇匦灾?,它允許一個(gè)(稱為子類或派生)繼承另一個(gè)(稱為父
    的頭像 發(fā)表于 08-20 11:02 ?1272次閱讀

    傾斜光柵的魯棒性優(yōu)化

    摘要 ** 由于制造過程中潛在的不準(zhǔn)確性,對(duì)于一個(gè)好的光柵設(shè)計(jì)來說,面對(duì)光柵參數(shù)的微小變化,提供穩(wěn)健的結(jié)果是至關(guān)重要的。VirtualLab Fusion為光學(xué)工程師提供各種工具,可以將
    發(fā)表于 08-12 18:38

    聊聊JVM如何優(yōu)化

    進(jìn)行優(yōu)化。 1.JVM內(nèi)存模型 針對(duì)JAVA8的模型進(jìn)行討論,JVM的內(nèi)存模型主要分為幾個(gè)關(guān)鍵區(qū)域:堆、方法區(qū)、程序計(jì)數(shù)器、虛擬機(jī)棧和本地方法棧。堆內(nèi)存進(jìn)一步細(xì)分為年輕代、老年代,年輕代按其特性又分為E區(qū),S1和S2區(qū)。關(guān)于內(nèi)存模型的一些細(xì)節(jié)就
    的頭像 發(fā)表于 08-05 17:49 ?486次閱讀
    聊聊JVM如何<b class='flag-5'>優(yōu)化</b>

    華納云:java web和java有什么區(qū)別java web和java有什么區(qū)別

    Java Web和Java是兩個(gè)不同的概念,它們?cè)诠δ?、用途和?shí)現(xiàn)方式上存在一些區(qū)別,下面將詳細(xì)介紹它們之間的區(qū)別。 1. 功能和用途: – Java是一種編程語言,它提供
    的頭像 發(fā)表于 07-16 13:35 ?825次閱讀
    華納云:<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么區(qū)別<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么區(qū)別

    JDK8升級(jí)JDK11最全實(shí)踐干貨來了

    1、前言 截至目前(2023年),Java8發(fā)布至今已有9年,2018年9月25日,Oracle發(fā)布Java11,這是Java8之后的首個(gè)LTS版本。那么從JDK
    的頭像 發(fā)表于 06-25 14:51 ?457次閱讀
    JDK<b class='flag-5'>8</b>升級(jí)JDK11最全實(shí)踐干貨來了

    請(qǐng)問handle的寫法

    遇到一行代碼 YDLidar *laser = lidarCreate(); //Create a handle to this Lidar. 請(qǐng)問這行代碼是什么意思,句柄的寫法怎么是這樣的呀
    發(fā)表于 05-18 10:42

    在官方庫中發(fā)現(xiàn)GPIOx-&gt;ODR這種寫法,是什么意思,怎么中間會(huì)有-&gt;?

    本人新手,剛接觸st的芯片 在官方庫中發(fā)現(xiàn)GPIOx->ODR這種寫法,不知道這是什么意思,怎么中間會(huì)有->? 請(qǐng)論壇上高手予以幫忙解答
    發(fā)表于 05-10 06:48

    的網(wǎng)線能用六水晶頭嗎

    網(wǎng)線不能使用六或七水晶頭。雖然六和七水晶頭與八網(wǎng)線在結(jié)構(gòu)上有相似之處,但是它們的性
    的頭像 發(fā)表于 04-18 11:45 ?1879次閱讀

    java實(shí)現(xiàn)多線程的幾種方式

    多種實(shí)現(xiàn)多線程的方式,本文將詳細(xì)介紹以下幾種方式: 1.繼承Thread 2.實(shí)現(xiàn)Runnable接口 3.Callable和Future 4.線程池 5.Java 8
    的頭像 發(fā)表于 03-14 16:55 ?722次閱讀

    淺談代碼優(yōu)化與過度設(shè)計(jì)

    本文記錄了作者從“代碼優(yōu)化”到“過度設(shè)計(jì)”的典型思考過程,這過程中涉及很多Java的語法糖及設(shè)計(jì)模式的東西,很典型,能啟發(fā)思考,遂記錄下來。 有一天Review師妹的代碼,看到一行很難看的代碼
    的頭像 發(fā)表于 01-19 10:05 ?553次閱讀
    淺談代碼<b class='flag-5'>優(yōu)化</b>與過度設(shè)計(jì)