利用Java8Optional避免空指針異常的示例-創(chuàng)新互聯(lián)

小編給大家分享一下利用Java8 Optional避免空指針異常的示例,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

十年的龍華網(wǎng)站建設(shè)經(jīng)驗(yàn),針對設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。網(wǎng)絡(luò)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整龍華建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“龍華網(wǎng)站設(shè)計(jì)”,“龍華網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

前言

空指針是我們最常見也最討厭的異常,為了防止空指針異常,你不得在代碼里寫大量的非空判斷。

Java 8引入了一個(gè)新的Optional類。用于避免空指針的出現(xiàn),也無需在寫大量的if(obj!=null)這樣的判斷了,前提是你得將數(shù)據(jù)用Optional裝著,它就是一個(gè)包裹著對象的容器。

都說沒有遇到過空指針異常的程序員不是Java程序員,null確實(shí)引發(fā)過很多問題。Java 8中引入了一個(gè)叫做java.util.Optional的新類可以避免null引起的諸多問題。

我們看看一個(gè)null引用能導(dǎo)致哪些危害。首先創(chuàng)建一個(gè)類Computer,結(jié)構(gòu)如下圖所示:

利用Java8 Optional避免空指針異常的示例

當(dāng)我們調(diào)用如下代碼會(huì)怎樣?

String version = computer.getSoundcard().getUSB().getVersion();

上述代碼看似是沒有問題的,但是很多計(jì)算機(jī)(比如,樹莓派)其實(shí)是沒有聲卡的,那么調(diào)用getSoundcard()方法可定會(huì)拋出空指針異常了。

一個(gè)常規(guī)的但是不好的的方法是返回一個(gè)null引用來表示計(jì)算機(jī)沒有聲卡,但是這就意味著會(huì)對一個(gè)空引調(diào)用getUSB()方法,顯然會(huì)在程序運(yùn)行過程中拋出控制異常,從而導(dǎo)致程序停止運(yùn)行。想想一下,當(dāng)你的程序在客戶端電腦上運(yùn)行時(shí),突然出現(xiàn)這種錯(cuò)是多尷尬的一件事?

偉大計(jì)算機(jī)科學(xué)Tony Hoare曾經(jīng)寫到:"我認(rèn)為null引用從1965年被創(chuàng)造出來導(dǎo)致了十億美元的損失。當(dāng)初使用null引用對我大的誘惑就是它實(shí)現(xiàn)起來方便。"

那么該怎么避免在程序運(yùn)行時(shí)會(huì)出現(xiàn)空指針異常呢?你需要保持警惕,并且不斷檢查可能出現(xiàn)空指針的情況,就像下面這樣:

String version = "UNKNOWN";
if(computer != null)
 {
 Soundcard soundcard = computer.getSoundcard();
 if(soundcard != null){
  USB usb = soundcard.getUSB();
  if(usb != null){
   version = usb.getVersion();
  }
  }
 }

然而,你可以看到上述代碼有太多的null檢查,整個(gè)代碼結(jié)構(gòu)變得非常丑陋。但是我們又不得不通過這樣的判斷來確保系統(tǒng)運(yùn)行時(shí)不會(huì)出現(xiàn)空指針。如果在我們的業(yè)務(wù)代碼中出現(xiàn)大量的這種空引用判斷簡直讓人惱火,也導(dǎo)致我們代碼的可讀性會(huì)很差。

如果你忘記檢查要給值是否為空,null引用也是存在很大的潛在問題。這篇文章我將證明使用null引用作為值不存在的表示是不好的方法。我們需要一個(gè)更好的表示值不存在的模型,而不是再使用null引用。

Java 8引入了一個(gè)新類叫做java.util.Optional<T> ,這個(gè)類的設(shè)計(jì)的靈感來源于Haskell語言和Scala語言。這個(gè)類可以包含了一個(gè)任意值,像下面圖和代碼表示的那樣。你可以把Optional看做是一個(gè)有可能包含了值的值,如果Optional不包含值那么它就是空的,下圖那樣。

利用Java8 Optional避免空指針異常的示例

public class Computer {
 private Optional<Soundcard> soundcard;
 public Optional<Soundcard> getSoundcard() { ... }
 ...
}

public class Soundcard {
 private Optional<USB> usb;
 public Optional<USB> getUSB() { ... }

}

public class USB{
 public String getVersion(){ ... }
}

上述代碼展現(xiàn)了一臺(tái)計(jì)算機(jī)有可能包換一個(gè)聲卡(聲卡是有可能存在也有可能不存在)。聲卡也是有可能包含一個(gè)USB端口的。這是一種改善方法,該模型可以更加清晰的反映一個(gè)被給定的值是可以不存在的。

但是該怎么處理Optional<Soundcard>這個(gè)對象呢?畢竟,你想要獲取的是USB的端口號(hào)。很簡單,Optional類包含了一些方法來處理值是否存在的狀況。和null引用相比Optional類迫使你在你要做值是否相關(guān)處理,從而避免了空指針異常。

需要說明的是Optional類并不是要取代null引用。相反地,是為了讓設(shè)計(jì)的API更容易被理解,當(dāng)你看到一個(gè)函數(shù)的簽名時(shí),你就可以判斷要傳遞給這個(gè)函數(shù)的值是不是有可能不存在。這就促使你要打開Optional類來處理確實(shí)值的狀況了。

采用Optional模式

啰嗦了這么多,來看一些代碼吧!我們先看一下怎么使用Optional改寫傳統(tǒng)的null引用檢測后是什么樣子。在這邊文章的末尾你將會(huì)明白怎么使用Optional。

String name = computer.flatMap(Computer::getSoundcard)
       .flatMap(Soundcard::getUSB)
       .map(USB::getVersion)
       .orElse("UNKNOWN");

創(chuàng)建Optional對象

可以創(chuàng)建一個(gè)空的Optional對象:

Optional<Soundcard> sc = Optional.empty();

接下來是創(chuàng)建一個(gè)包含非null值的Optional:

SoundCard soundcard = new Soundcard();
Optional<Soundcard> sc = Optional.of(soundcard);

如果聲卡null,空指針異常會(huì)立即被拋出(這比在獲取聲卡屬性時(shí)才拋出要好)。

通過使用ofNullable,你可以創(chuàng)建一個(gè)可能包含null引用的Optional對象:

Optional<Soundcard> sc = Optional.ofNullable(soundcard);

如果聲卡是null 引用,Optional對象就是一個(gè)空的。

對Optional中的值的處理

既然現(xiàn)在已經(jīng)有了Optional對象,你可以調(diào)用相應(yīng)的方法來處理Optional對象中的值是否存在。和進(jìn)行null檢測相比,我們可以使用ifPresent()方法,像下面這樣:

Optional<Soundcard> soundcard = ...;
soundcard.ifPresent(System.out::println);

這樣就不必再做null檢測,如果Optional對象是空的,那么什么信息將不會(huì)打印出來。

你也可以使用isPresent()方法查看Optional對象是否真的存在。另外,還有一個(gè)get()方法可以返回Optional對象中的包含的值,如果存在的話。否則會(huì)拋出一個(gè)NoSuchElementException異常。這兩個(gè)方式可以像下面這樣搭配起來使用,從而避免異常:

if(soundcard.isPresent()){
 System.out.println(soundcard.get());
}

但是這種方式不推薦使用(它和null檢測相比沒有什么改進(jìn)),下面我們將會(huì)探討一下工作慣用的方式。

返回默認(rèn)值和相關(guān)操作

當(dāng)遇到null時(shí)一個(gè)常規(guī)的操作就是返回一個(gè)默認(rèn)值,你可以使用三元表達(dá)式來實(shí)現(xiàn):

Soundcard soundcard = maybeSoundcard != null ? maybeSoundcard : new Soundcard("basic_sound_card");

使用Optional對象的話,你可以orElse()使用重寫,當(dāng)Optional是空的時(shí)候orElse()可以返回一個(gè)默認(rèn)值:

Soundcard soundcard = maybeSoundcard.orElse(new Soundcard("defaut"));

類似地,當(dāng)Optional為空的時(shí)候也可以使用orElseThrow()拋出異常:

Soundcard soundcard = 
 maybeSoundCard.orElseThrow(IllegalStateException::new);

使用filter過濾特定的值

我們常常會(huì)調(diào)用一個(gè)對象的方法來判斷它的一下屬性。比如,你可能需要檢測USB端口號(hào)是否是某個(gè)特定值。為了安全起見,你需要檢查指向USB的醫(yī)用是否是null,然后再調(diào)用getVersion()方法,像下面這樣:

USB usb = ...;
if(usb != null && "3.0".equals(usb.getVersion())){
 System.out.println("ok");
}

如果使用Optional的話可以使用filter函數(shù)重寫:

Optional<USB> maybeUSB = ...;
maybeUSB.filter(usb -> "3.0".equals(usb.getVersion())
     .ifPresent(() -> System.out.println("ok"));

filter方法需要一個(gè)predicate對向作為參數(shù)。如果Optional中的值存在并且滿足predicate,那么filter函數(shù)將會(huì)返回滿足條件的值;否則,會(huì)返回一個(gè)空的Optional對象。

使用map方法進(jìn)行數(shù)據(jù)的提取和轉(zhuǎn)化

一個(gè)常見的模式是提取一個(gè)對象的一些屬性。比如,對于一個(gè)Soundcard對象,你可能需要獲取它的USB對象,然后判斷它的的版本號(hào)。通常我們的實(shí)現(xiàn)方式是這樣的:

if(soundcard != null){
 USB usb = soundcard.getUSB();
 if(usb != null && "3.0".equals(usb.getVersion()){
 System.out.println("ok");
 }
}

我們可以使用map方法重寫這種檢測null,然后再提取對象類型的對象。

Optional<USB> usb = maybeSoundcard.map(Soundcard::getUSB);

這個(gè)和使用stream的map函數(shù)式一樣的。使用stream需要給map函數(shù)傳遞一個(gè)函數(shù)作為參數(shù),這個(gè)傳遞進(jìn)來的函數(shù)將會(huì)應(yīng)用于stream中的每個(gè)元素。當(dāng)stream時(shí)空的時(shí)候,什么也不會(huì)發(fā)生。

Optional中包含的值將會(huì)被傳遞進(jìn)來的函數(shù)轉(zhuǎn)化(這里是一個(gè)從聲卡中獲取USB的函數(shù))。如果Optional對象時(shí)空的,那么什么也不會(huì)發(fā)生。

然后,我們結(jié)合map方法和filter方法過濾掉USB的版本號(hào)不是3.0的聲卡。

maybeSoundcard.map(Soundcard::getUSB)
  .filter(usb -> "3.0".equals(usb.getVersion())
  .ifPresent(() -> System.out.println("ok"));

這樣我們的代碼開始變得像有點(diǎn)像開始我們給出的樣子,沒有了null檢測。

使用flatMap函數(shù)傳遞Optional對象

現(xiàn)在已經(jīng)介紹了一個(gè)可以使用Optional重構(gòu)代碼的例子,那么我們應(yīng)該如何使用安全的方式實(shí)現(xiàn)下面代碼呢?

String version = computer.getSoundcard().getUSB().getVersion();

注意上面的代碼都是從一個(gè)對象中提取另一個(gè)對象,使用map函數(shù)可以實(shí)現(xiàn)。在前面的文章中我們設(shè)置了Computer中包含的是一個(gè)Optional<Soundcard>對象,Soundcard包含的是一個(gè)Optional<USB>對象,因此我們可以這么重構(gòu)代碼

String version = computer.map(Computer::getSoundcard)
     .map(Soundcard::getUSB)
     .map(USB::getVersion)
     .orElse("UNKNOWN");

不幸的是,上面的代碼會(huì)編譯錯(cuò)誤,那么為什么呢?computer變量是Optional<Computer>類型的,所以它調(diào)用map函數(shù)是沒有問題的。但是getSoundcard()方法返回的是一個(gè)Optional<Soundcard>的對象,返回的是Optional<Optional<Soundcard>>類型的對象,進(jìn)行了第二次map函數(shù)的調(diào)用,結(jié)果調(diào)用getUSB()函數(shù)就變成非法的了。

下面的圖描述了這種場景:

利用Java8 Optional避免空指針異常的示例

map函數(shù)的源碼實(shí)現(xiàn)是這樣的:

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

可以看出map函數(shù)還會(huì)再調(diào)用一次Optional.ofNullable() , 從而導(dǎo)致返回Optional<Optional<Soundcard>>
Optional提供了flatMap這個(gè)函數(shù),它的設(shè)計(jì)意圖是當(dāng)對Optional對象的值進(jìn)行轉(zhuǎn)化(就像map操作)然后一個(gè)兩級(jí)Optional壓縮成一個(gè)。下面的圖展示了Optional對象通過調(diào)用map和flatMap進(jìn)行類型轉(zhuǎn)化的不同:

利用Java8 Optional避免空指針異常的示例

因此我們可以這樣寫:

String version = computer.flatMap(Computer::getSoundcard)
     .flatMap(Soundcard::getUSB)
     .map(USB::getVersion)
     .orElse("UNKNOWN");

第一個(gè)flatMap保證了返回的是Optional<Soundcard>而不是Optional<Optional<Soundcard>> ,第二個(gè)flatMap實(shí)現(xiàn)了同樣的功能從而返回的是 Optional<USB> 。注意第三次調(diào)用了map() ,因?yàn)?code>getVersion()返回的是一個(gè)String對象而不是一個(gè)Optional對象。

我們終于把剛開始使用的嵌套null檢查的丑陋代碼改寫了可讀性高的代碼,也避免了空指針異常的出現(xiàn)的代碼。

看完了這篇文章,相信你對“利用Java8 Optional避免空指針異常的示例”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

新聞名稱:利用Java8Optional避免空指針異常的示例-創(chuàng)新互聯(lián)
文章轉(zhuǎn)載:http://muchs.cn/article4/dsgiie.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化做網(wǎng)站、虛擬主機(jī)網(wǎng)站導(dǎo)航、App設(shè)計(jì)、網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化