新同事一來就把整個專案中可能存在NPE解決了

大家好,我是李哥。

JDK1。6和JDK1。8是主流的兩個大版本,目前市場上用的最多最多的依然是JDK1。8。所以,我們有必要聊一聊Java8的一些新特性。

深入理解lambda的奧秘

深入理解Stream之原理剖析

深入理解Stream之foreach原始碼解析

深入淺出NPE神器Optional(本文)

談談介面預設方法與靜態方法(待)

深入淺出重複註解與型別註解(待)

深入淺出JVM元空間metaspace(待)

深入理解CompletableFuture非同步程式設計(待)

今天我們先來聊聊

Optional深度解析

新同事一來就把整個專案中可能存在NPE解決了

防止NPE:程式設計師的基本素養

身為一名Java程式設計師,大家可能都有這樣的經歷: 呼叫一個方法得到了返回值卻不能直接將返回值作為引數去呼叫別的方法。我們首先要判斷這個返回值是否為null,只有在非空的前提下才能將其作為其他方法的引數。

沒錯,防止NPE(NullPointerException),這就是我們作為一名開發工程師的基本素養。

還有很多場景都可能發生NPE,一一列舉:

返回型別為基本資料型別,return 包裝資料型別的物件時,自動拆箱有可能產生 NPE。比如public int f() { return null;}

資料庫的查詢結果可能為 null。

集合裡的元素即使 isNotEmpty,取出的資料元素也可能為 null,因為null也可以作為一個元素存入集合之中。

遠端呼叫返回物件時,一律要求進行空指標判斷,防止 NPE。

對於 Session 中獲取的資料,建議 NPE 檢查,避免空指標。

級聯呼叫 obj。getA()。getB()。getC();一連串呼叫,易產生 NPE。建議使用 JDK8 的 Optional 類來防止 NPE 問題。

Optional

Optional 類主要解決的問題是臭名昭著的空指標異常(NullPointerException)

Optional 最有用武之地的是在那些“更接近資料”的地方,在問題空間中代表實體的物件上,代表月亮消滅你(NPE)。

新同事一來就把整個專案中可能存在NPE解決了

。。。扯遠了。。。我們迴歸正題。。。

Optional類的doc註釋如下:

一個容器物件,可能包含也可能不包含非空值。如果存在值,isPresent() 將返回 true,而 get() 將返回該值。

empty

返回一個空的可選例項。 此 Optional 沒有值。

Optional內部包含一個空的例項物件,value值為null,我們可以使用empty這個靜態方法來獲取。

of

返回具有指定當前非空值的 Optional。

// 呼叫工廠方法建立Optional例項Optional lige = Optional。of(“Lige”);// 傳入引數為null,會丟擲NullPointerException。Optional someNull = Optional。of(null);

透過靜態方法of可以獲取一個Optional例項,值得注意的是此時不允許傳入null,如果傳入引數為null,則丟擲NullPointerException 。

如果你覺得你也不確定是否為null,那麼應該使用ofNullable。

ofNullable

返回一個描述指定值的 Optional,如果非 null,否則返回一個空 Optional。

ofNullable與of方法相似,唯一的區別是可以接受引數為null的情況。

get

如果此 Optional 中存在值,則返回該值,否則丟擲 NoSuchElementException。

可見如果當前Optional物件的value是null,呼叫get方法則會丟擲異常,這一點需要注意。

isPresent

如果存在值,則返回 true,否則返回 false。

if (optional。isPresent()) { Object object = optional。get(); // TODO: 2022/8/18 todo something}

一般情況下isPresent與get配合使用。

ifPresent

如果存在值,則使用該值呼叫指定的使用者,否則不執行任何操作。

optional。ifPresent(obj -> { // TODO: 2022/8/18 todo something});

直接使用get有可能丟擲異常,如果使用isPresent加以判斷後再使用get略顯複雜,使用ifPresent能有效解決問題,讓我們的重心偏向於有資料的情況,去對資料進行處理。在這裡使用lambda表示式,程式碼簡潔,可讀性也好。

對於lambda表示式,瞭解更多可以參考:深入理解lambda的奧秘

filter

如果存在一個值,並且該值與給定的謂詞匹配,則返回一個描述該值的 Optional,否則返回一個空的 Optional。

這裡的filter與Stream內的filter沒有任何關係,不過實現思路都是一致的,都是對當前物件做過濾動作。

optional。filter(it -> { if (it。equals(“lige”)) { return true; } return false;})。ifPresent(it -> { System。out。println(it);});

map與flatMap

如果存在值,則將提供的 Optional-bearing 對映函式應用於它,返回該結果,否則返回空 Optional。 此方法類似於 map(Function),但提供的對映器是其結果已經是 Optional 的對映器,如果被呼叫,flatMap 不會用附加的 Optional 包裝它。

新同事一來就把整個專案中可能存在NPE解決了

可以看到,map與flatMap非常的相似,只不過是傳入的Function內的泛型物件不一樣。功能也是一樣的,針對型別做轉換。

Optional flatMapResult = optional。flatMap(new Function>() { @Override public Optional apply(String s) { return Optional。of(s。toLowerCase()); }});flatMapResult。ifPresent(it -> { System。out。println(it);});Optional mapResult = optional。map(new Function() { @Override public String apply(String s) { return s。toLowerCase(); }});mapResult。ifPresent(System。out::println);

orElse、orElseGet、orElseThrow

上面我們說完了if存在如何處理,當然也需要else的處理邏輯了,那就是這仨兄弟了。

原理都是如果Optional的value不為null則返回,如果為null則返回指定值或執行指定邏輯。

orElse與orElseGet都是返回指定值,orElseThrow會丟擲指定異常。

String orElseResult = optional。orElse(“orElseResult ligejishu”);String orElseGetResult1 = optional。orElseGet(new Supplier() { @Override public String get() { return “orElseGetResult1 ligejishu”; }});String orElseGetResult2 = optional。orElseGet(() -> “orElseGetResult2 ligejishu”);try { String orElseThrowResult1 = optional。orElseThrow(new Supplier() { @Override public Throwable get() { return new RuntimeException(“ligejishu throw RuntimeException。”); } }); String orElseThrowResult2 = optional。orElseThrow((Supplier) () -> new RuntimeException(“ligejishu throw RuntimeException。”));} catch (Throwable e) { e。printStackTrace();}

總結

雖然 Optional 是 Java 8 為了支援流式程式設計才引入的,但其實它是一個通用的工具。並且在Java9中對Optional進行了持續最佳化。

實際上,在所有地方都使用 Optional 是沒有意義的,但是對於不確定的因素的地方檢查一下是否為 null 是非常有意義的。

新同事一來就把整個專案中可能存在NPE解決了