@value 默認值為null_JAVA8之妙用Optional解決判斷Null為空的問題

引言在文章的開頭,先說下NPE問題,NPE問題就是,我們在開發中經常碰到的NullPointerException.假設我們有兩個類,他們的UML類圖如下圖所示

9a925ccc36bb62c01d3848f11771a061.png

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

user.getAddress().getProvince();

這種寫法,在user為null時,是有可能報NullPointerException異常的。為了解決這個問題,于是采用下面的寫法

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

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

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

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

這四個函數之間具有相關性,因此放在一組進行記憶。

先說明一下,Optional(T value),即構造函數,它是private權限的,不能由外部調用的。其余三個函數是public權限,供我們所調用。那么,Optional的本質,就是內部儲存了一個真實的值,在構造的時候,就直接判斷其值是否為空。好吧,這么說還是比較抽象。直接上Optional(T value)構造函數的源碼,如下圖所示

ae9c2f8ff3223793a08cd94f9263fed0.png

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

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

也就是說of(T value)函數內部調用了構造函數。根據構造函數的源碼我們可以得出兩個結論:

  • 通過of(T value)函數所構造出的Optional對象,當Value值為空時,依然會報NullPointerException。

  • 通過of(T value)函數所構造出的Optional對象,當Value值不為空時,能正常構造Optional對象。

除此之外呢,Optional類內部還維護一個value為null的對象,大概就是長下面這樣的

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

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

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

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

好吧,大家應該都看得懂什么意思了。相比較of(T value)的區別就是,當value值為null時,of(T value)會報NullPointerException異常;ofNullable(T value)不會throw Exception,ofNullable(T value)直接返回一個EMPTY對象。

那是不是意味著,我們在項目中只用ofNullable函數而不用of函數呢?

不是的,一個東西存在那么自然有存在的價值。當我們在運行過程中,不想隱藏NullPointerException。而是要立即報告,這種情況下就用Of函數。但是不得不承認,這樣的場景真的很少。博主也僅在寫junit測試用例中用到過此函數。

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

這三個函數放一組進行記憶,都是在構造函數傳入的value值為null時,進行調用的。orElse和orElseGet的用法如下所示,相當于value值為null時,給予一個默認值:

@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;
}

這兩個函數的區別:當user值不為null時,orElse函數依然會執行createUser()方法,而orElseGet函數并不會執行createUser()方法,大家可自行測試。

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

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

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

這兩個函數放在一組記憶,這兩個函數做的是轉換值的操作。

直接上源碼

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

這兩個函數,在函數體上沒什么區別。唯一區別的就是入參,map函數所接受的入參類型為Function super T, ? extends U>,而flapMap的入參類型為Function super T, Optional>。

在具體用法上,對于map而言:

如果User結構是下面這樣的

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

這時候取name的寫法如下所示

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

對于flatMap而言:

如果User結構是下面這樣的

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

這時候取name的寫法如下所示

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

4、isPresent()和ifPresent(Consumer super T> consumer)

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

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
}

因為這樣寫,代碼結構依然丑陋。博主會在后面給出正確寫法

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

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 方法接受一個 Predicate 來對 Optional 中包含的值進行過濾,如果包含的值滿足條件,那么還是返回這個 Optional;否則返回 Optional.empty。

用法如下

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

如上所示,如果user的name的長度是小于6的,則返回。如果是大于6的,則返回一個EMPTY對象。

實戰使用例一

在函數方法中

以前寫法

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("取值錯誤");?
????}

JAVA8寫法

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

例二

比如,在主程序中

以前寫法

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;
???????????????????});
}

其他的例子,不一一列舉了。不過采用這種鏈式編程,雖然代碼優雅了。但是,邏輯性沒那么明顯,可讀性有所降低,大家項目中看情況酌情使用。

zjhred

blog.csdn.net/zjhred/article/details/84976734

4fd2cdbd994cedeaf437597977b593a4.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/534636.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/534636.shtml
英文地址,請注明出處:http://en.pswp.cn/news/534636.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql百萬數據根據索引查詢_mysql創建多列索引查詢百萬表數據的性能優化經驗分享...

最近發現最代碼網站中的收到的評論&#xff0c;提到我的&#xff0c;心情被贊的查詢異常緩慢&#xff0c;通過nginx日志發現響應時間快的在5s&#xff0c;慢的有13s&#xff0c;終于忍無可忍花時間來解決了。執行explain之后的截圖如下&#xff1a;可以看到possible_keys中有很…

php用到的mysql語句_PHP中常用到的一些MySQL語句_php

在php開發中&#xff0c;經常會使用到mysql語句&#xff0c;下面就為您列舉了一些經常使用的MySQL語句&#xff0c;希望對您平時的學習和開發工作能起到些許的作用。MySQL語句顯示數據庫或表:show databases;//然后可以use database_name;show tables;MySQL語句更改表名:alter …

mysql執行計劃性能_MySQL SQL性能分析Explain執行計劃

一. 執行計劃返回信息詳解①. 執行計劃所含字段輸出列含義id查詢標識select_type查詢類型table查詢涉及的表partitions匹配到的分區信息type連接類型possible_keys可能選擇的索引key實際使用的索引key_len實際使用的索引的長度ref和索引進行比較的列rows需要被檢索的大致行數fi…

mysql定時作業_mysql 讓一個存儲過程定時作業的代碼(轉)

1、在mysql 中建立一個數據庫 test1語句&#xff1a;create database test12、創建表examinfocreate table examinfo(id int auto_increment not null,endtime datetime,primary key(id));3 插入數據&#xff1a;insert into examinfo values(‘1‘,‘2011-4-23 23:26:50‘);4 …

table虛線邊框_web前端工程師7天0基礎到精通(TABLE+CSS制作《互聯世紀網》)

項目七 項目實踐&#xff1a;TABLECSS制作《互聯世紀網》實踐目標1、 熟悉CSS屬性2、 熟練運用CSS屬性控制網頁樣式3、 熟悉網頁制作流程項目簡介&#xff1a;通過上一章節的學習&#xff0c;我們了解了CSS樣式能更加方便、有效地控制網頁結構和布局網頁元素&#xff0c;大大提…

mixamo骨骼_mixamo動作庫的模型和動作綁定控制器的方法-上集

1.首先從網站下載帶調好動作的文件fbx&#xff0c;我們將fbx場景文件轉換成c4d場景文件。沒轉換之前轉換之后選擇場次&#xff0c;在文件菜單里找到當前場次到新文檔&#xff01;2.我們將模型重置為Tpose方便后續操作&#xff0c;沒重置之前模型為k好的動作模式不能使用選中權重…

mysql 101_MySQL 調優/優化的 101 個建議!

原文&#xff1a;http://www.monitis.com/blog/101-tips-to-mysql-tuning-and-optimization/MySQL是一個強大的開源數據庫。隨著MySQL上的應用越來越多&#xff0c;MySQL逐漸遇到了瓶頸。這里提供 101 條優化 MySQL 的建議。有些技巧適合特定的安裝環境&#xff0c;但是思路是相…

數據安全:保護個人隱私和企業機密的關鍵

在當今數字化時代&#xff0c;數據已經成為了一種寶貴的資源。無論是個人還是企業&#xff0c;都離不開數據的支持。然而&#xff0c;隨著數據的不斷增長和廣泛應用&#xff0c;數據安全問題也日益突出。數據泄露、黑客攻擊、網絡詐騙等安全事件層出不窮&#xff0c;給個人和企…

python批量跑plsql_python實現自動化報表(Oracle/plsql/Excel/多線程)

# -*- coding: utf-8 -*-# Create time: 2019-10-16# Update time: 2019-11-28# Version: 1.0# Version: 2.0 增加多線程/出錯自動重新運行模塊# 導入模塊import cx_Oracleimport osimport pandas as pdimport pandas.io.sql as sqlimport timeimport openpyxlimport xlwings a…

mysql 配置郵件_SQL 郵件配置篇

exec sp_configure show advanced options,1RECONFIGURE WITHOVERRIDEgoexec sp_configure database mail xps,1RECONFIGURE WITHOVERRIDEgo--2.創建郵件帳戶信息EXECmsdb..Sysmail_add_account_spACCOUNT_NAME OCTMamiETL,--郵件帳戶名稱EMAIL_ADDRESS OCTMamiETL163.com,--發…

python 抽獎 配音樂_抖音上超好聽的神曲音樂,Python教你一次性下載

不知道什么時候開始&#xff0c;中國出現了南抖音、北快手的互文格局(東市買駿馬&#xff0c;西市買鞍韉…)。剛才提到了&#xff0c;之前比較喜歡刷抖音&#xff0c;對于我這種佛系程序猿&#xff0c;看網上這些整容妹子基本一個樣。喜歡抖音主要是兩個初衷&#xff0c;學做菜…

mysql批量寫入100萬數據_Mysql數據庫實踐操作之————批量插入數據(100萬級別的數據)-阿里云開發者社區...

第一種方法&#xff1a;使用insert into 插入從Redis每次獲取100條數據&#xff0c;根據條件去插入到Mysql數據庫中&#xff1a;條件&#xff1a;如果當前隊列中的值大于1000條&#xff0c;則會自動的條用該方法&#xff0c;該方法每次獲取從隊列的頭部每次獲取100掉數據插入到…

mysql多客戶端數據不同步_一種多終端設備上的數據同步方法

一種多終端設備上的數據同步方法【技術領域】[0001] 屬于移動通信技術領域&#xff0c;特別是涉及基于離網環境下多種移動終端設備之間的數 據同步的方法。 技術背景[0002] 90年代未&#xff0c;數據同步始于有線連接&#xff0c;如MAC機作為數據中心&#xff0c;與終端設備(iP…

oem監控mysql_OEM12c 安裝配置MySQL Plug-in用來監控MySQL

Plug-in--注冊信息[roottest agent]# /oem/emcli setup -urlhttps://omsdb.localdomain:7301/em -usernamesysmanOracle Enterprise Manager 12c 3.Copyright (c) 1996, 2013 Oracle Corporation and/or its affiliates. All rights reserved.The configuration directory &quo…

怎么利用迭代器寫入mysql_range()是什么?為什么不生產迭代器?

本篇文章給大家帶來的內容是關于range()是什么&#xff1f;為什么不生產迭代器&#xff1f;有一定的參考價值&#xff0c;有需要的朋友可以參考一下&#xff0c;希望對你有所幫助。迭代器是 23 種設計模式中最常用的一種(之一)&#xff0c;在 Python 中隨處可見它的身影&#x…

java 流式_Java開發筆記(七十二)Java8新增的流式處理

通過前面幾篇文章的學習&#xff0c;大家應能掌握幾種容器類型的常見用法&#xff0c;對于簡單的增刪改和遍歷操作&#xff0c;各容器實例都提供了相應的處理方法&#xff0c;對于實際開發中頻繁使用的清單List&#xff0c;還能利用Arrays工具的asList方法給清單對象做初始化賦…

java保留二位小數_java使double保留兩位小數的多方法 java保留兩位小數

復制代碼代碼如下:mport java.text.DecimalFormat;DecimalFormat df new DecimalFormat("######0.00");double d1 3.23456double d2 0.0;double d3 2.0;df.format(d1);df.format(d2);df.format(d3);3個結果分別為:復制代碼代碼如下:3.230.002.00java保留兩位…

linux java jar打包_【Java】Java程序打包成jar包在Linux上運行

當需要把在Windows上開發的Java程序用在Linux上運行時&#xff0c;就需要吧該Java程序打包成jar包上傳到Linux上去運行。1.Java程序用MyEclipse打包成可運行的jar包(1)在MyEclipse中選中需要打包的項目&#xff0c;點擊右鍵&#xff0c;選擇&#xff1a;Export... 如下圖所示&a…

java匿名對象 回收_Java 匿名對象

我們知道一般實例化一個對象的格式&#xff0c;如下&#xff1a;Car car new Car();其中&#xff0c;變量名 car 就是 new Car() 這個對象的名字。car 是引用類型的變量&#xff0c;它的值存放的是對象的引用(或地址)&#xff0c;通過 car 這個變量我們就可以間接使用對象。那…

java int 正則表達式_java正則表達式

Java正則表達式正則表達式定義了字符串的模式。正則表達式可以用來搜索、編輯或處理文本。正則表達式并不僅限于某一種語言&#xff0c;但是在每種語言中有細微的差別。Java正則表達式和Perl的是最為相似的。java.util.regex包主要包括以下三個類&#xff1a;Pattern類&#xf…