Java 是如何優雅地處理NPE問題的


1. 前言

對於 Java 開發者來說,null 是一個令人頭疼的類型,一不小心就會發生 NPE (空指針) 問題。也是 Java 語言為人詬病的一個重要原因之一。在我們消除可惡的 NPE 問題之前我們要回顧一下 Java 中 null 的概念。

2. Java 中的 null

翻譯自 Oracle Java 文檔

Java語言中有兩種類型,一種是 基本類型 ,另一種是 引用類型。還有一種沒有名字的特殊類型,即表達式 null
由於 null 類型沒有名稱,所以不可能聲明為 null 類型的變量或者轉換為 null 類型。
null 引用是null 類型表達式唯一可能的值。
null 引用可以轉換為任意引用類型。
事實上,程序員可以忽略null類型,可以認為null僅僅是一個可以成為任何引用類型的特殊符號。

從上面的描述我們可以了解到,其實 null 僅僅是一個關鍵字標識量,既不是一種類型也不算對象,無法直接聲明 null 和被轉換為 null,僅僅只能被引用,null 可以轉換為任何引用類型。當一個 Java 引用類型對象被引用為 null 時代表當前對象不引用對象,並沒有為其分配內存。 這也是我們在沒有引用的對象上調用方法出現空指針的根本原因。
大多數情況下 Java 開發者使用 null 是為了表示某種不存在的意思。

3. NPE 問題的解決

很多時候我們對數據是否存在有自己的期望,但是這種期望並不能直接被我們掌控,一個返回值為 null 所表達的意思並不明確過於模糊,往往通過是否判斷為 null 來規避空指針問題。於是 Google 工程師在他們的 Guava 工具類庫中設計了 Optional<T> 來解決 null 不可控的問題。 讓你在不得不使用 null 的時候,可以更加簡便明確的使用 null 並幫助你避免直接使用 null 帶來的問題。
Java 8 將此設計吸收。我們可以直接使用 Java 提供的 Optional 來解決空指針問題。接下來我們來研究一下 Java 8 中的 Optional

4. Java 8 中的 Optional

Java 8 中的 Optional 是一個可選值的包裝類。它的意義不僅僅幫我們簡化了 NPE 問題的處理,同時也是 Java 函數式編程的一個重要輔助。 我們接下來將對其 API 進行講解以幫助你在實際開發中使用他們。

4.1 Optional 聲明

Optional 只能通過靜態方法來聲明。它提供了三個靜態方法:

empty() 返回一個值為 nullOptional 實例

  Optional<Object> empty = Optional.empty();

of(T) 返回一個值不為 nullOptional 實例

 Optional<String> nonNull = Optional.of("Felordcn");

ofNullable() 返回一個值可能為 nullOptional 實例

 // value 值來自其它不確定的來源
 String value = SomeApi.source();
 // 可能為 null 
 Optional<String> nullable = Optional.ofNullable(value);
 // 也可能不為 null 
 Optional<String>  hasValue = Optional.ofNullable(value);
    

4.2 其它方法

isPresent() 如果值存在則返回 true,否則返回 false 。如果 Optional 值不確定,可使用該方法進行安全校驗

 Optional<String> nonNull = Optional.of("Felordcn");
  // true 
  boolean present =nonNull.isPresent();

get() 獲取 Optional 中的值,如果為空會拋出 NoSuchElementException 異常

 Optional<String> nonNull = Optional.of("Felordcn");
  // Felordcn 
  String str = nonNull.get();

ifPresent(Consumer) 如果值存在則該值被消費函數 Consumer 消費 , 否則不做任何事情。 isPresent() 加強版


      // 非空打印出字符串 
      nullable.ifPresent(System.out::println);
     
     //等同於
      if (nullable.isPresent()) {
                 System.out.println(nonNull);
      }

filter(Predicate) 如果值滿足斷言函數 Predicate 則返回該 Optional,否則返回 Optional.empty()

 Optional<String> nonNull = Optional.of("Felordcn");
 Optional<String> felord = nonNull.filter(s -> s.startsWith("Felord"));
 // str = "Felordcn"
 String str = felord.get();

map(Function) 獲取元素某個屬性的 Optional 。 如果該屬性為 null 返回 Optional.empty() ,否則返回對應值的 Optional

 Optional<User> userOpt = Optional.ofNullable(user);
// username 為空 則為 空 Optional 
 Optional<String> usernameOpt = userOpt.map(User::getUsername);

flatMap(Function) 有時候我們會返回 Optional<Optional<T>> 非常不便於處理,我們需要將元素展開,可使用該方法處理,參考 Stream Api 中的相關方法

orElse(other) 如果 Optional 的值存在,返回 Optional, 否則指定一個 Optional

orElseGet(Supplier) 如果 Optional 的值存在,返回 Optional, 否則指定一個執行 Supplier 函數來獲取值

orElseThrow(Supplier<? extends Throwable>) 如果 Optional 的值存在,返回 Optional, 否則拋出一個指定 Supplier 函數提供的異常

4.3 Java 9 中的新API

or(Supplier) orElseGet 的改進類型。不單單返回具體的值,而可以函數式的返回 Optional

stream()OptionalStream 打通

ifPresentOrElse(Consumer) ifPresent 方法提供了有值后的消費邏輯而沒有值的邏輯沒有提供入口。新方法 ifPresentOrElse 彌補了這一缺陷

5. Optional 的使用誤區

Optional 很香但是也不能濫用。一個危險的舉動就是將 Optional 作為入參傳遞給方法。 因為入參是不可控的,你無法保證入參中的 Optional 是否為 null。這恰恰違背了 Optional 的本意。所以盡量在表達式中使用 Optional 或者在返回值中使用,而不是在方法的參數中使用 Optional

6. 總結

今天對 Optional 進行講解。從 Optional 的設計本意到其常用的方法。我們也對 OptionalJava 9 中的新 API 進行了介紹。另外 Optional 也不是萬能的,合理的使用才能發揮其優勢。希望今天的文章對你有用。

關注公眾號:Felordcn獲取更多資訊

個人博客:https://felord.cn


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM