歡迎光臨
每天分享高質量文章

在 Java 8 中避免 Null 檢查

(點選上方公眾號,可快速關註)


來源:ostatsu,

www.oschina.net/translate/avoid-null-checks-in-java

如何預防 Java 中著名的 NullPointerException 異常?這是每個 Java 初學者遲早會問到的關鍵問題之一。而且中級和高階程式員也在時時刻刻規避這個錯誤。其是迄今為止 Java 以及很多其他程式語言中最流行的一種錯誤。

Null 取用的發明者 Tony Hoare 在 2009 年道歉,並稱這種錯誤為他的十億美元錯誤。

我將其稱之為自己的十億美元錯誤。它的發明是在1965 年,那時我用一個面向物件語言(ALGOL W)設計了第一個全面的取用型別系統。我的目的是確保所有取用的使用都是絕對安全的,編譯器會自動進行檢查。但是我未能抵禦住誘惑,加入了 Null 取用,僅僅是因為實現起來非常容易。它導致了數不清的錯誤、漏洞和系統崩潰,可能在之後 40 年中造成了十億美元的損失。

無論如何,我們必須要面對它。所以,我們到底能做些什麼來防止 NullPointerException 異常呢?那麼,答案顯然是對其新增 null 檢查。由於 null 檢查還是挺麻煩和痛苦的,很多語言為了處理 null 檢查添加了特殊的語法,即空合併運運算元 —— 其在像 Groovy 或 Kotlin 這樣的語言中也被稱為 Elvis 運運算元。

不幸的是 Java 沒有提供這樣的語法糖。但幸運的是這在 Java 8 中得到了改善。這篇文章介紹瞭如何利用像 lambda 運算式這樣的 Java 8 新特性來防止編寫不必要的 null 檢查的幾個技巧。

在 Java 8 中提高 Null 的安全性

我已經在另一篇文章中說明瞭我們可以如何利用 Java 8 的 Optional 型別來預防 null 檢查。下麵是那篇文章中的示例程式碼。

http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/

假設我們有一個像這樣的類層次結構:

class Outer {

    Nested nested;

    Nested getNested() {

        return nested;

    }

}

class Nested {

    Inner inner;

    Inner getInner() {

        return inner;

    }

}

class Inner {

    String foo;

    String getFoo() {

        return foo;

    }

}

解決這種結構的深層巢狀路徑是有點麻煩的。我們必須編寫一堆 null 檢查來確保不會導致一個 NullPointerException:

Outer outer = new Outer();

if (outer != null && outer.nested != null && outer.nested.inner != null) {

    System.out.println(outer.nested.inner.foo);

}

我們可以透過利用 Java 8 的 Optional 型別來擺脫所有這些 null 檢查。map 方法接收一個 Function 型別的 lambda 運算式,並自動將每個 function 的結果包裝成一個 Optional 物件。這使我們能夠在一行中進行多個 map 操作。Null 檢查是在底層自動處理的。

Optional.of(new Outer())

    .map(Outer::getNested)

    .map(Nested::getInner)

    .map(Inner::getFoo)

    .ifPresent(System.out::println);

還有一種實現相同作用的方式就是透過利用一個 supplier 函式來解決巢狀路徑的問題:

Outer obj = new Outer();

resolve(() -> obj.getNested().getInner().getFoo());

    .ifPresent(System.out::println);

呼叫 obj.getNested().getInner().getFoo()) 可能會丟擲一個 NullPointerException 異常。在這種情況下,該異常將會被捕獲,而該方法會傳回 Optional.empty()。

public static Optional resolve(Supplier resolver) {

    try {

        T result = resolver.get();

        return Optional.ofNullable(result);

    }

    catch (NullPointerException e) {

        return Optional.empty();

    }

}

請記住,這兩個解決方案可能沒有傳統 null 檢查那麼高的效能。不過在大多數情況下不會有太大問題。

像往常一樣,上面的示例程式碼都託管在 GitHub

https://github.com/winterbe/java8-tutorial

看完本文有收穫?請轉發分享給更多人

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂