如何在 Java8 中風騷走位避開空指針異常

文章整理翻譯自 winterbe.com/posts/2015/…html

要說 Java 編程中哪一個異常是你印象最深入的,那 NullPointerException 空指針能夠說是臭名昭著的。不要說初級程序員會碰到, 即便是中級,專家級程序員稍不留神,就會掉入這個坑裏。java

Null 引用的發明者 Tony Hoare 曾在 2009 年做出道歉聲明,聲明中表示,到目前爲止,空指針異常大約給企業已形成數十億美圓的損失。git

下面是 Tony Hoare 的原話:程序員

我將 Null 引用的設計稱爲是一個數十億美圓的錯誤。1965 那年,我正在用面嚮對象語言(ALGOL W) 設計首個功能全面的系統。當時個人考量是,確保全部被使用的引用都是安全的,編譯器會自動進行檢查。可是,我沒有抵住誘惑,加入了 Null 引用,僅僅是爲了實現起來省事。這以後,它致使了數不清的 bug、錯誤和系統崩潰,也爲企業致使了不可估量的損失。github

事已至此,咱們必須學會面對它。So, 咱們要如何防止空指針異常呢?sql

惟一的辦法就是對可能爲 Null 的對象添加檢查。可是 Null 檢查是繁瑣且痛苦的。因此一些比較新的語言爲了處理 Null 檢查,特地添加了特殊的語法,如空合併運算符編程

GroovyKotlin 這樣的語言中也被稱爲 Elvis 運算符。安全

不幸的是,在老版本的 Java 中並無提供這樣的語法糖。Java8 中在這方面作了改進。因此,這篇文章就特地來介紹一下如何在 Java8 中利用新特性來編寫防止 NullPointerException的發生。微信


★★★ 若是此教程有幫助到你, 去小哈的 GitHub 幫忙 Star 一下吧, 謝謝啦! 傳送門 ★★★ide


Java8 中如何增強對 Null 對象的檢查?

在上篇文章 Java8 新特性指導手冊 中簡單的提了一下如何經過 Optional 類來對對象作空校驗。接下來,咱們再細說一下:

在業務系統中,對象中嵌套對象是常常發生的場景,以下示例代碼:

// 最外層對象
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);
}
複製代碼

經過 Optional

在 Java8 中,咱們有更優雅的解決方式,那就是使用 Optional是說,咱們能夠在一行代碼中,進行流水式的 map 操做。而 map 方法內部會自動進行空校驗

Optional.of(new Outer())
    .map(Outer::getNested)
    .map(Nested::getInner)
    .map(Inner::getFoo
    .ifPresent(System.out::println); // 若是不爲空,最終輸出 foo 的值
複製代碼

經過 suppiler 函數自定義加強 API

上面這種方式我的感受仍是有點囉嗦,咱們能夠利用 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 的值
複製代碼

最後

你須要知道的是,上面這兩個解決方案並沒傳統的 null 檢查性能那麼高效。但在絕大部分業務場景下,捨棄那麼一丟丟的性能來方便編碼,是徹底可取, 除非是那種對性能有嚴格要求的場景,咱們纔不建議使用。

我的以爲,真要拿這點性能說事,還不如去優化優化 sql 語句,業務邏輯等。

小哈的微信公衆號,優質文章第一時間奉上,歡迎關注哦

相關文章
相關標籤/搜索