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() 返回一個值為 null
的 Optional
實例
Optional<Object> empty = Optional.empty();
of(T) 返回一個值不為 null
的 Optional
實例
Optional<String> nonNull = Optional.of("Felordcn");
ofNullable() 返回一個值可能為 null
的 Optional
實例
// 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() 將 Optional
和 Stream
打通
ifPresentOrElse(Consumer) ifPresent
方法提供了有值后的消費邏輯而沒有值的邏輯沒有提供入口。新方法 ifPresentOrElse
彌補了這一缺陷
5. Optional 的使用誤區
Optional
很香但是也不能濫用。一個危險的舉動就是將 Optional
作為入參傳遞給方法。 因為入參是不可控的,你無法保證入參中的 Optional
是否為 null
。這恰恰違背了 Optional
的本意。所以盡量在表達式中使用 Optional
或者在返回值中使用,而不是在方法的參數中使用 Optional
。
6. 總結
今天對 Optional
進行講解。從 Optional
的設計本意到其常用的方法。我們也對 Optional
在 Java 9 中的新 API 進行了介紹。另外 Optional
也不是萬能的,合理的使用才能發揮其優勢。希望今天的文章對你有用。
關注公眾號:Felordcn獲取更多資訊