要說 Java 編程中哪一個異常是你印象最深入的,那 NullPointerException
空指針能夠說是臭名昭著的。不要說初級程序員會碰到, 即便是中級,專家級程序員稍不留神,就會掉入這個坑裏。程序員
Null
引用的發明者Tony Hoare 曾在 2009 年做出道歉聲明,聲明中表示,到目前爲止,空指針異常大約給企業已形成數十億美圓的損失。編程
下面是 Tony Hoare 的原話:安全
我將 Null 引用的設計稱爲是一個數十億美圓的錯誤。1965 那年,我正在用面嚮對象語言(ALGOL W) 設計首個功能全面的系統。當時個人考量是,確保全部被使用的引用都是安全的,編譯器會自動進行檢查。可是,我沒有抵住誘惑,加入了 Null 引用,僅僅是爲了實現起來省事。這以後,它致使了數不清的 bug、錯誤和系統崩潰,也爲企業致使了不可估量的損失。架構
事已至此,咱們必須學會面對它。So, 咱們要如何防止空指針異常呢?函數
惟一的辦法就是對可能爲 Null 的對象添加檢查。可是 Null 檢查是繁瑣且痛苦的。因此一些比較新的語言爲了處理 Null 檢查,特地添加了特殊的語法,如空合併運算符。學習
在Groovy 或Kotlin 這樣的語言中也被稱爲 Elvis
運算符。spa
不幸的是,在老版本的 Java 中並無提供這樣的語法糖。Java8 中在這方面作了改進。因此,這篇文章就特地來介紹一下如何在 Java8 中利用新特性來編寫防止 NullPointerException
的發生。設計
在上篇文章 Java8 新特性指導手冊 中簡單的提了一下如何經過 Optional
類來對對象作空校驗。接下來,咱們再細說一下:在此我向你們推薦一個架構學習交流裙。交流學習裙號:687810532,裏面會分享一些資深架構師錄製的視頻錄像指針
在業務系統中,對象中嵌套對象是常常發生的場景,以下示例代碼:code
// 最外層對象 class Outer { Nested nested; Nested getNested() { return nested; } } // 第二層對象 class Nested { Inner inner; Inner getInner() { return inner; } } // 最底層對象 class Inner { String foo; String getFoo() { return foo; } } 複製代碼
業務中,假設咱們須要獲取 Outer
對象對底層的 Inner
中的 foo
屬性,咱們必須寫一堆的非空校驗,來防止發生 NullPointerException
:
// 繁瑣的代碼 Outer outer = new Outer(); if (outer != null && outer.nested != null && outer.nested.inner != null) { System.out.println(outer.nested.inner.foo); } 複製代碼
在 Java8 中,咱們有更優雅的解決方式,那就是使用 Optional
是說,咱們能夠在一行代碼中,進行流水式的 map
操做。而 map 方法內部會自動進行空校驗 :
Optional.of(new Outer()) .map(Outer::getNested) .map(Nested::getInner) .map(Inner::getFoo .ifPresent(System.out::println); // 若是不爲空,最終輸出 foo 的值 複製代碼
上面這種方式我的感受仍是有點囉嗦,咱們能夠利用 suppiler
函數來出一個終極解決方案:
public static <T> Optional<T> resolve(Supplier<T> resolver) { try { T result = resolver.get(); return Optional.ofNullable(result); } catch (NullPointerException e) { // 可能會拋出空指針異常,直接返回一個空的 Optional 對象 return Optional.empty(); } } 複製代碼
利用上面的 resolve
方法來重構上述的非空校驗代碼段:
Outer obj = new Outer(); // 直接調用 resolve 方法,內部作空指針的處理 resolve(() -> obj.getNested().getInner().getFoo()); .ifPresent(System.out::println); // 若是不爲空,最終輸出 foo 的值 複製代碼