Java8新特性之空指針異常的克星Optional類的實(shí)現(xiàn)
Java8新特性系列我們已經(jīng)介紹了Stream、Lambda表達(dá)式、DateTime日期時(shí)間處理,最后以“NullPointerException” 的克星Optional類的講解來收尾。
背景
作為開發(fā)人員每天與NullPointerException斗智斗勇。每接收到參數(shù)或調(diào)用方法獲得值得判斷一下是否為null。稍不留意,空指針異常就像幽靈一樣出現(xiàn)了。
這篇文章我們來學(xué)習(xí)Java8是如何通過Optional類來避免空指針異常的。
先來看一下不使用Optional類時(shí),我們?yōu)榱朔乐筃ullPointerException會(huì)怎么處理。
public String getParentName(Person son) { if (son != null) { Person parent = son.getParent(); if (parent != null) { return parent.getUsername(); } else { return "--"; } } return "--"; }
為了防止出現(xiàn)異常,需要不停的判斷對(duì)象是否為null。但如果業(yè)務(wù)邏輯比較復(fù)雜,會(huì)涌現(xiàn)出大量的ifelse??此七壿嬁b密,但易讀性卻并不高。
為了解決相關(guān)問題,在Effective Java中建議若方法返回類型為集合,則通過返回空集合以避免 NullPointerException,真是煞費(fèi)苦心。
先看一下上面的代碼使用Optional之后會(huì)變成什么樣子。
public String getParentNameWithOptional(Person son) { return Optional.ofNullable(son).map(Person::getParent).map(Person::getUsername).orElse("--"); }
對(duì)照一下代碼,看看神不神奇?!
Optional類簡(jiǎn)介
java.util.Optional類的引入很好的解決空指針異常,類聲明如下:
public final class Optional<T> {}
java.util.Optional類是一個(gè)封裝了Optional值的容器對(duì)象,Optional值可以為null,如果值存在,調(diào)用isPresent()方法返回true,調(diào)用get()方法可以獲取值。
通過源代碼會(huì)發(fā)現(xiàn),它并沒有實(shí)現(xiàn)java.io.Serializable接口,因此應(yīng)避免在類屬性中使用,防止意想不到的問題。
除了Optional類之外,還擴(kuò)展了一些常用類型的Optional對(duì)象,比如:OptionalDouble、OptionalInt、OptionalLong。用法基本上相似。
下面通過具體的操作和功能來了解Optional類。
創(chuàng)建Optional對(duì)象
創(chuàng)建Optional對(duì)象有三種方法:empty()、of()、ofNullable(),均為靜態(tài)方法。
如果Optional對(duì)象沒有值則用empty()方法。
Optional empty = Optional.empty();
如果確定Optional對(duì)象的值不為null,則可用of()方法。
Optional stringOptional = Optional.of("Hello 公眾號(hào):程序新視界");
如果不確定Optional對(duì)象的值是否為null,則可用ofNullable()。比如上面,不確定Person對(duì)象是不否null,就用了ofNullable()方法。當(dāng)然,也可以直接給該方法傳null。
Optional ofNullOptional = Optional.ofNullable(null);
此時(shí),通過調(diào)用其isPresent方法可以查看該Optional中是否值為null。
boolean bool = ofNullOptional.isPresent(); System.out.println(bool);
此時(shí)如果直接調(diào)用get方法獲取值,則會(huì)拋出異常。
ofNullOptional.get();
get獲取Optional中的值
通過get方法可獲取Optional中的值,但如果值為null,則會(huì)拋出異常。
Optional ofNullOptional = Optional.ofNullable(null); ofNullOptional.get();
異常信息:
java.util.NoSuchElementException: No value present at java.util.Optional.get(Optional.java:135) ...
此時(shí),需要另外一個(gè)方法的輔助:isPresent()。該方法可判定Optional中是否有值,如果有則返回true,如果沒有則返回false。
Optional ofNullOptional = Optional.ofNullable(null); boolean bool = ofNullOptional.isPresent(); if(bool){ ofNullOptional.get(); }
map獲取Optional中的值
對(duì)于對(duì)象操作,也可以通過map來獲取值,最開始簡(jiǎn)化的例子就是如此。
Optional<Person> sonOptional = Optional.ofNullable(son); System.out.println(sonOptional.map(Person::getUsername));
map方法,如果有值,則對(duì)其執(zhí)行調(diào)用映射函數(shù)得到返回值。如果返回值不為null,則創(chuàng)建包含映射返回值的Optional作為map方法返回值,否則返回空Optional。
flatMap獲取Optional中的值
如果有值,則返回Optional類型返回值,否則返回空Optional。flatMap與map方法類似。但flatMap中的mapper返回值必須是Optional。調(diào)用結(jié)束時(shí),flatMap不會(huì)對(duì)結(jié)果用Optional封裝。
Optional<Person> sonOptional = Optional.ofNullable(son); sonOptional.flatMap(OptionalTest::getOptionalPerson);
調(diào)用的是當(dāng)前類OptionalTest的另外一個(gè)方法:
public static Optional<Person> getOptionalPerson(Person person){ return Optional.ofNullable(person); }
orElse獲取Optional中的值
orElse方法,如果有值就返回,否則返回一個(gè)給定的值作為默認(rèn)值;
Optional.empty().orElse("--");
上面這種情況就會(huì)返回“–”。
在此,這種操作與三目運(yùn)算效果一樣。
str != null ? str : "--"
orElseGet獲取Optional中的值
orElseGet()方法與orElse()方法作用類似,但生成默認(rèn)值的方式不同。該方法接受一個(gè)Supplier<? extends T>函數(shù)式接口參數(shù),用于生成默認(rèn)值;
Optional.empty().orElseGet(() -> { String a = "關(guān)注"; String b = "公眾號(hào):程序新視界"; return a b; });
很顯然,這里可以處理更多的業(yè)務(wù)邏輯。
orElseThrow獲取Optional中的值
orElseThrow()方法與get()方法類似,當(dāng)值為null時(shí)調(diào)用會(huì)拋出NullPointerException異常,但該方法可以指定拋出的異常類型。
Optional.empty().orElseThrow(()-> new RuntimeException("請(qǐng)先關(guān)注公眾號(hào)!"));
此時(shí)打印異常信息為:
Optional.empty().orElseThrow(()-> new RuntimeException("請(qǐng)先關(guān)注公眾號(hào)!"));
判斷并執(zhí)行操作
ifPresent方法,可對(duì)值進(jìn)行判斷然后打印,接收參數(shù)為Consumer<? super T>函數(shù)式接口。
Optional.of("公眾號(hào):程序新視界").ifPresent(System.out::println);
當(dāng)然,也可以在函數(shù)中執(zhí)行其他復(fù)雜操作:
Optional.of("公眾號(hào):程序新視界").ifPresent((val)->{ System.out.println("歡迎關(guān)注" val); });
filter()方法過濾
filter()方法可用于判斷Optional對(duì)象是否滿足給定條件,一般用于條件過濾:
Optional.of("公眾號(hào):程序新視界").filter((val)->{ return val.contains("程序新視界"); }); // 簡(jiǎn)化寫法 Optional.of("公眾號(hào):程序新視界").filter((val)-> val.contains("程序新視界"));
使用誤區(qū)
關(guān)于使用Optional的誤區(qū)有以下:
- 正確的使用創(chuàng)建方法,不確定是否為null時(shí)盡量選擇ofNullable方法。
- 避免用在成員變量上(原因上面已經(jīng)提到);
- 避免直接調(diào)用Optional對(duì)象的get和isPresent方法;
最后一條可能難理解,試想一下如果先用isPresent方法獲得是否存在,然后決定是否調(diào)用get方法和之前的ifelse判斷并無二致。
Java8提倡函數(shù)式編程,新增的許多API都可以用函數(shù)式編程表示,Optional類也是其中之一。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
MySQL中關(guān)鍵字UNION和UNION ALL的區(qū)別
本文主要介紹了MySQL中關(guān)鍵字UNION和UNION ALL的區(qū)別,深入探討UNION和UNION ALL的定義、用法、主要區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06SpringMVC Mock測(cè)試實(shí)現(xiàn)原理及實(shí)現(xiàn)過程詳解
這篇文章主要介紹了SpringMVC Mock測(cè)試實(shí)現(xiàn)原理及實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10Java?SM2加密相關(guān)實(shí)現(xiàn)與簡(jiǎn)單原理詳解
SM2算法可以用較少的計(jì)算能力提供比RSA算法更高的安全強(qiáng)度,而所需的密鑰長(zhǎng)度卻遠(yuǎn)比RSA算法低,這篇文章主要給大家介紹了關(guān)于Java?SM2加密相關(guān)實(shí)現(xiàn)與簡(jiǎn)單原理的相關(guān)資料,需要的朋友可以參考下2024-01-01Java中實(shí)現(xiàn)日期時(shí)間字符串轉(zhuǎn)換為Date對(duì)象的方法
在 Java 編程中,日期時(shí)間的處理是一項(xiàng)常見且重要的任務(wù),無論是數(shù)據(jù)存儲(chǔ)、日志記錄還是業(yè)務(wù)邏輯處理,準(zhǔn)確地表示和操作日期時(shí)間都是不可或缺的,本文給大家介紹了Java中實(shí)現(xiàn)日期時(shí)間字符串轉(zhuǎn)換為Date對(duì)象的方法,需要的朋友可以參考下2025-01-01java中抽象類、抽象方法、接口與實(shí)現(xiàn)接口實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于java中抽象類、抽象方法、接口與實(shí)現(xiàn)接口的相關(guān)資料,文中通過示例代碼將四者介紹的非常詳細(xì),并且簡(jiǎn)單介紹了抽象類和接口的區(qū)別,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11