成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

正確使用Java8中的Optional,它遠比我們想象的優秀

開發 前端
怎樣做才能避免不期而至的NullPointerException?通常,在需要的地方添加null的檢查,所以我們的代碼多了很多的判斷是否為null的驗證,影響代碼結構,甚至有時不加思索是否需要驗證也會統一加上非空判斷,來避免不可預知的空值,防止生產環境造成損失!

前言

我常說學習一定要有目的,首先發現問題,或者不便之處,然后尋找解決方案,解決方案可能有很多,我們要選擇好的方法來使用

這篇文章介紹JDK8推出的Optional容器,會從以下幾點展開:

  • 現在編程的問題或者說痛點是什么
  • 通過案例演示:解決方案有哪些,Optional怎么解決
  • Optional系統用法
  • Optional的錯誤用法
  • Optional總結

由此一起來認識Optional的正確使用方式,遠比我們想象的強大,好用,看很多文章和教程都在講API,個人感覺調用一個方法誰不會?它到底好在哪才是最重要的,我發布的文章都秉承發現問題,解決問題的理念展開,好了,不吹了,精彩的要來了!

編程痛點

作為Java程序員遇到NullPointerException是非常痛苦的,這可能是我們遇到的最多的異常了

前后端聯調:嗨!哥們,你這500啥意思呀?

后端:先是沉思,這怎么會有空指針?對前端說:哥們等1分鐘,馬上解決,我可不能說空指針,我可是老開發了!說空指針多沒面子。

產生過這種無奈的請在評論區大聲說出來!無論是新手還是專家,在NullPointerException面前可謂眾生平等

我們編程時經常承受:寫了類型檢查,值判斷,最終沒想到竟然是一個null的痛苦,毫不留情的甩出來一個令人厭煩的NullPointerException,比如:

系統中用戶,有些用戶進行了實名認證,擁有身份證信息,有些用戶并沒有完成實名認證就沒有身份證信息【不要深究設計是否合理,僅僅是舉例講解Optional知識點】

用戶類:

public class User {

private Long id;

private String name;
// 身份證對象
private IdCard idCard;
// getter、setter、toString
}

身份證類:

public class IdCard {
private Long id;
// 身份證號碼
private String idNum;
// getter、setter、toString
}

測試類:獲取用戶的身份證號碼

public class OptionalMain {

public static void main(String[] args) {
// 創建用戶對象
User user = new User();
// 調用一系列get方法獲取身份證號碼
// 因為調用 getIdCard()時并沒有身份證對象為null,再調用getIdNum方法則出現 NullPointerException
String idNum = user.getIdCard().getIdNum();
System.out.println(idNum);
}
}

運行結果:

如果user是傳遞進來的,傳進來的user也有可能是null

解決方案

怎樣做才能避免不期而至的NullPointerException?通常,在需要的地方添加null的檢查,所以我們的代碼多了很多的判斷是否為null的驗證,影響代碼結構,甚至有時不加思索是否需要驗證也會統一加上非空判斷,來避免不可預知的空值,防止生產環境造成損失!并且添加的方式往往各有不同:

嵌套判斷:

public class OptionalMain {
public static void main(String[] args) {
User user = new User();
// 判斷user是否為null
if(user != null) {
IdCard idCard = user.getIdCard();
// 判斷 idCard 是否為null
if(idCard != null) {
// 獲取身份證號碼
System.out.println(idCard.getIdNum());
}else {
System.out.println("未實名認證!");
}
}else {
System.out.println("該用戶不存在!");
}
}
}

逐個判斷:

public class OptionalMain {

/**
* 獲取身份證號碼
* @param user:用戶
* @return:身份證號碼
*/
public static String getUserIdcardNum(User user) {
// 判斷用戶是否為空
if(user == null) {
return "無此用戶";
}
// 判斷是否實名認證
if(user.getIdCard() == null) {
return "該用戶未實名認證";
}
// 返回身份證號碼,如果:要對身份證號碼進行操作,也要對idNum進行非空判斷
return user.getIdCard().getIdNum();
}

public static void main(String[] args) {
// 創建用戶對象
User user = new User();
// 1、調用獲取身份證方法,有用戶但未實名
System.out.println("******未認證******");
String userIdcardNum1 = getUserIdcardNum(user);
System.out.println("結果:" + userIdcardNum1);
// 2、傳遞空用戶
System.out.println("******空用戶******");
String userIdcardNum2 = getUserIdcardNum(null);
System.out.println("結果:" + userIdcardNum2);
// 3、創建身份證對象
IdCard idCard = new IdCard();
idCard.setId(1L);
idCard.setIdNum("411481199611111516");
user.setIdCard(idCard);
// 傳遞實名認證的用戶
System.out.println("******已認證******");
String userIdcardNum3 = getUserIdcardNum(user);
System.out.println("結果:" + userIdcardNum3);
}
}

運行結果:

如果有其他要求,就要做更多的非空判斷,影響代碼的連貫性,凈判斷空值了

一旦忘記判斷某一個值是否為空,就又要和 NullPointerException 偶遇了,它并不是女朋友,而是最不想遇見的【債主】

null值帶來的問題

  • NullPointerException是目前Java程序開發中最典型的異常,有些書中稱其為錯誤之源,個人覺得有點夸張,你覺著呢?
  • 各種非空判斷,讓代碼變的冗余,閱讀性很糟糕,非空判斷對業務實現是毫無意義的
  • null值本身也毫無意義,可以認為是給對象一個【錯誤的默認值】
  • null可以被賦值給任意的引用數據類型,如果是分布式系統,該值被傳遞到另一個服務中,無法知道最初的它是什么類型,也無法對其進行賦值
  • Java為了簡化語言,摒棄了指針的概念,但是 NullPointerException是個例外

Optional的出現

Java團隊結合Haskell和Scala語言對null值的處理方式,在JDK8時推出Optional類來專門處理空值問題,當然該類并不是為了避免我們去寫!=null的非空判斷,他功能很強,配合Lambda表達式更香

源碼注釋

/**
* A container object which may or may not contain a non-null value.
一個可以包含或不包含非空值的容器對象
* If a value is present, {@code isPresent()} will return {@code true} and
* {@code get()} will return the value.
如果存在值,isPresent()方法會返回true,通過get()方法返回值
*
* <p>Additional methods that depend on the presence or absence of a contained
* value are provided, such as {@link #orElse(java.lang.Object) orElse()}
* (return a default value if value not present) and
* {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
* of code if the value is present).
提供了取決于是否存在包含值的其他方法,比如orElse,如果值不存在,則返回默認值 并且 可以通過ifPresent()
判斷值是否存在,存在則執行代碼塊
* <p>This is a <a href="../lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code Optional} may have unpredictable results and should be avoided.
這是一個基于值的類,應避免使用于身份敏感操作【這里應該意思是:對象是否存在不確定的敏感操作】(包括引用 ==,哈希或同步)的實例可能會產生不可預測的結果
* @since

從Optional類的定義和聲明來看特點如下:

  • 是一個final類,不可被繼承,并且是一個泛型類
  • 該類是一個容器,可以用來存儲對象
  • 該類提供了一系列方法來判斷是否有值【isPresent()】和獲取值【get()】

Optional解決null問題

通過案例感受Optional處理null的套路:

  • 將可能為null,或者說允許為null的數據存儲進Optional容器中
  • 通過Optional的map、filter、flatMap方法對數據進行處理,獲取需要的對象屬性,用法和Stream相同
  • 如果數據為空了,可以返回一個自定義對象,或者拋出異常都可以,隨你所愿

User類:

public class User {

private Long id;

private String name;

// 將可能為null的對象放入Optional中
private Optional<IdCard> idCard;

// getter、setter、toString
}

IdCard類:

public class IdCard {
private Long id;
// 如果身份證號碼也允許為null,也可以放入Optional中【Optional<String>
// 但是實名認證了,身份證號碼就是必須的了不是嗎,
// 一旦使用了Optional,沒有身份證號碼時,也不會出現報錯,可能會出現數據錯誤,所以也不要濫用
private String idNum;
// getter、setter、toString
}

測試類:

public class OptionalMain {

/**
* 獲取身份證號碼
* @param user:用戶
* @return:身份證號碼
*/
public static String getUserIdcardNum(User user){
// 將User通過Optional.of() 方法 存儲進Optional
Optional<User> optionalUser = Optional.of(user);
// 通過map方法先獲取user中身份對象,orElse:如果沒有,返回一個自定義的Optional<IdCard>對象
Optional<IdCard> optionalIdCard = optionalUser.map(User::getIdCard).orElse(Optional.of(new IdCard()));
// 通過map方法獲取IdCard中的idNum,如果沒有返回 "無實名認證"字符串
String idNum = optionalIdCard.map(IdCard::getIdNum).orElse("無實名認證");
return idNum;
}

public static void main(String[] args){
User user = new User();
// 將user對象傳進方法中,該對象中的IdCard為null
System.out.println(getUserIdcardNum(user));
}
}

運行結果:

我們僅僅傳入了user對象,IdCard為null,通過getUserIdcardNum方法處理之后,返回定義的無實名認證,這里并沒有做if...else的判斷,這樣的代碼看起來更優雅,不是嗎?

總結來說:

  • 把對象放進Optional中,可以通過Optional提供的API來操作容器中的對象
  • 如:對象非空正常使用,我們可以通過get()方法獲取對象
  • 如果是空可以通過某些方法【orElse、orElseGet、orElseThrow】,返回自定義的結果,避免空指針異常出現
  • 通過一些判斷方法來判斷Optional中對象是否為null

接下來講解一下Optional中的API,系統認識,學習強大的Optional

Optional結構

Optional方法概覽

方法

作用

Optional.empty()

創建一個空的 Optional 實例

Optional.of(T t)

創建一個 Optional 實例,當 t為null時拋出異常

Optional.ofNullable(T t)

創建一個 Optional 實例,但當 t為null時不會拋出異常,而是返回一個空的實例

get()

獲取optional實例中的對象,當optional 容器為空時報錯

isPresent()

判斷optional是否為空,如果空則返回false,否則返回true

ifPresent(Consumer c)

如果optional不為空,則將optional中的對象傳給Comsumer函數

orElse(T other)

如果optional不為空,則返回optional中的對象;如果為null,則返回 other 這個默認值

orElseGet(Supplier<T> other)

如果optional不為空,則返回optional中的對象;如果為null,則使用Supplier函數生成默認值other

orElseThrow(Supplier<X> exception)

如果optional不為空,則返回optional中的對象;如果為null,則拋出Supplier函數生成的異常

filter(Predicate<T> p)

如果optional不為空,則執行斷言函數p,如果p的結果為true,則返回原本的optional,否則返回空的optional

map(Function<T, U> mapper)

如果optional不為空,則將optional中的對象 t 映射成另外一個對象 u,并將 u 存放到一個新的optional容器中

flatMap(Function< T,Optional<U>> mapper)

跟上面一樣,在optional不為空的情況下,將對象t映射成另外一個optional,區別在于:map會自動將u放到optional中,而flatMap則需要手動給u創建一個optional

強烈建議:打開編輯器,多翻閱源碼,對學習和編碼都有很大幫助,剛開始看不懂沒關系,量變產生質變

Optional 創建

通過Optional源碼發現:

  • 該類final修飾,不能被繼承,只有一個Object父類
  • 是一個泛型類,使用時為了類型安全指明泛型類型
  • 連個私有常量,供內部調用,其中value為Optional容器中存儲的對象
  • 兩個構造方法,無參和有參的都為私有,說明不能通過構造方法創建Optional對象,需要通過內部提供的【empty()、of(T t)、ofNullable(T t)】三個靜態方法創建,這種創建方式其實就是【工廠模式】


代碼實現:

// 創建一個包裝對象值為空的Optional對象
Optional<Object> optional1 = Optional.empty();

// 創建包裝對象值非空的Optional對象,如果傳入null則出現`NullPointerException`
Optional<String> optional2 = Optional.of("optional");

// 創建包裝對象值允許為空的Optional對象
Optional<Object> optional3 = Optional.ofNullable(null);

Optional其他API

get()

作用:獲取optional實例中的對象,當optional 容器為空時報錯

源碼:

  • 判斷value是否為null
  • 為null,拋出 NoSuchElementException("No value present")異常
  • 不為null,返回value

null值Optional:

// 創建值為null的Optional對象
Optional<String> optional = Optional.empty();
// get返回 NoSuchElementException("No value present")
String result = optional.get();
System.out.println(result);

非null值Optional:

// 創建值為:optional的Optional對象
Optional<String> optional = Optional.of("optional");
// 返回值 optional
String result = optional.get();
System.out.println(result);

isPresent()

作用:判斷optional是否為空,如果空則返回false,否則返回true

源碼:

代碼實現:

List<String> users = new ArrayList<>();
users.add("柯南");
users.add("佩奇");
users.add("喜洋洋");
Optional<List<String>> optional = Optional.of(users);
// 判斷并消費
optional.ifPresent(System.out::println);

orElse(T other)

作用:如果optional不為空,則返回optional中的對象;如果為null,則返回 other 這個默認值

源碼:

代碼實現:

User user = new User(1L,"格雷福斯");
// 1、存儲非null數據
Optional<User> userOptional = Optional.ofNullable(user);
// 獲取用戶名
String name1 = userOptional.orElse(new User(0L, "帥氣添甄")).getName();
// 非null,結果為:格雷福斯
System.out.println(name1);

// 2、存儲null數據
Optional<User> nullOptional = Optional.ofNullable(null);
String name2 = nullOptional.orElse(new User(0L, "帥氣添甄")).getName();
// 為null,結果:帥氣添甄
System.out.println(name2);

orElseGet(Supplierother)

作用:如果optional不為空,則返回optional中的對象;如果為null,則使用Supplier函數生成默認值other

源碼:

代碼實現:

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(user);
// 為null直接返回`Supplier`生產型函數接口返回的對象
String name = userOptional.orElseGet(() new User(0L, "添甄")).getName();
System.out.println(name);

orElseThrow(Supplierexception)

作用:如果optional不為空,則返回optional中的對象;如果為null,則拋出Supplier函數生成的異常

源碼:

代碼實現:

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(user);
// 為null直接返回`Supplier`生產型函數接口返回的對象
String name = userOptional.orElseGet(() new User(0L, "添甄")).getName();
System.out.println(name);

orElseThrow(Supplierexception)

作用:如果optional不為空,則返回optional中的對象;如果為null,則拋出Supplier函數生成的異常

源碼:

代碼實現:

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(user);
// 如果數據為null,拋出 指定異常
String name = userOptional.orElseThrow(() new RuntimeException("無數據")).getName();
System.out.println(name);

filter(Predicatep)

作用:如果optional不為空,則執行斷言函數p,如果p的結果為true,則返回原本的optional,否則返回空的optional

源碼:

代碼實現:

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(user);

// 過濾名字長度大于3,如果有值才輸出,沒值就不輸出
userOptional.filter(item -> item.getName().length() > 3).ifPresent(System.out::println);

map(Function mapper)

作用:如果optional不為空,則將optional中的對象 t 映射成另外一個對象 u,并將 u 存放到一個新的optional容器中,該方法與Stream的map作用一樣

源碼:

代碼實現:

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(user);

// 只獲取用戶名
String name = userOptional.map(User::getName).orElse("添甄");
System.out.println(name);

flatMap(Function< T,Optional> mapper)

作用:在optional不為空的情況下,將對象t映射成另外一個optional,17-flatMapmap接收的是U類型,而flatMap接收的是Optional<U>類型,返回也是需要放進Optional中

源碼:

代碼實現:

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(null);
Optional<String> optional = userOptional.flatMap(item -> Optional.ofNullable(item.getName()));
String name = optional.orElse("添甄");
System.out.println(name);

錯誤示范

獲取用戶名:

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(user);
// 判斷是否有值
if (userOptional.isPresent()) {
String name = userOptional.get().getName();
System.out.println(name);
}else {
System.out.println("無值");
}

通過調用isPresent方法判斷是否有值,這還是增加了判斷,破壞代碼結構

正確姿勢:

多用map,orElse,filter方法發揮Optional的作用

User user = new User(1L,"格雷福斯");
Optional<User> userOptional = Optional.ofNullable(user);
String name = userOptional.map(User::getName).orElse("無值");
System.out.println(name);

總結

  • Optional是一個用來解決null值,避免發生空指針異常的容器,配合Lambda表達式寫出優雅代碼
  • 靜態工廠方法Optional.empty()、Optional.of()以及Optional.ofNullable()創建Optional對象
  • Optional類包含多種方法,其中map、flatMap、filter,它們在概念上與Stream類中對應的方法十分相似
  • 使用Optional能幫助你開發出更便于閱讀和簡介的程序
  • 多使用Optional中的方法給定默認值,比如map、orElse等方法來避免過多的if判斷

文章出自:??石添的編程哲學??,如有轉載本文請聯系【石添的編程哲學】今日頭條號。

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2017-10-31 20:45:07

JavaJava8Optional

2019-05-21 06:34:53

暗網網絡攻擊網絡安全

2022-04-12 14:59:45

加密貨幣比特幣環保

2021-09-05 23:54:55

人工智能機器語言

2025-02-25 08:36:56

2022-10-08 08:16:32

數據庫Oracle數據

2021-01-04 08:39:26

JAVA8OptionalNPE

2013-08-20 10:26:34

加密安全

2021-02-18 16:06:43

JavaStream代碼

2017-09-23 15:28:32

JavaOptional方法

2022-03-28 18:10:40

微服務架構

2023-03-26 19:58:25

ChatGPT技術架構

2025-06-26 08:10:00

Java8函數

2022-08-11 16:37:10

DeepMindAI人工智能

2014-04-11 12:49:00

Java8Java8教程

2022-07-20 16:39:37

AI數據

2010-01-18 10:27:20

2014-08-21 09:30:09

2024-01-17 08:20:20

AI人工智能AGI

2024-01-31 08:53:01

Java數組代碼
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 伊人在线 | 黄网站免费观看 | 一区二区三区四区在线视频 | 波多野结衣二区 | 亚洲视频精品在线 | 国产精品视频网 | 亚洲图片视频一区 | 在线观看成年视频 | 精品福利在线 | 做a视频在线观看 | 久久综合亚洲 | 91欧美| 欧洲亚洲一区二区三区 | 成人精品免费视频 | 日韩在线资源 | 午夜一区二区三区视频 | 色香蕉在线 | 91久久久久久久久久久 | 男女视频在线观看 | 成人国内精品久久久久一区 | 亚洲精品免费在线观看 | 久久免费观看一级毛片 | 天天久久| 伊人激情网 | 日韩视频在线一区 | 一区二区高清不卡 | 在线视频一区二区三区 | 国产精品99久久久久久宅男 | 亚洲精品456 | 日韩精品在线观看一区二区 | 黄色免费网 | 国产精品成人一区二区三区夜夜夜 | 久久天堂网 | 国产精品免费一区二区三区 | 国产色在线 | 免费大黄视频 | 中文字幕 亚洲一区 | 天天影视网天天综合色在线播放 | av国产精品毛片一区二区小说 | 中文在线a在线 | 国产精品久久久久久久免费观看 |