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

1. 前言

對於 Java 開發者來講,null 是一個使人頭疼的類型,一不當心就會發生 NPE (空指針) 問題。也是 Java 語言爲人詬病的一個重要緣由之一。在咱們消除可惡的 NPE 問題以前咱們要回顧一下 Java 中 null 的概念。html

2. Java 中的 null

翻譯自 Oracle Java 文檔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 只能經過靜態方法來聲明。它提供了三個靜態方法:oracle

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 值不肯定,可以使用該方法進行安全校驗spa

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

相關文章
相關標籤/搜索