空指針異常一直是困擾Java程序員的問題,也是我們必須要考慮的。當業務代碼中充滿了if else判斷null 的時候程序變得不再優雅,在Java8中提供了Optional類為我們解決NullPointerException。
我們先來看看這段代碼有什么問題?
1
2
3
4
5
6
7
8
9
|
class
User {
String name;
public
String getName() {
return
name;
}
}
public
static
String getUserName(User user){
return
user.getName();
}
|
這段代碼看起來很正常,每個User都會有一個名字。所以調用getUserName方法會發生什么呢? 實際這是不健壯的程序代碼,當User對象為null的時候會拋出一個空指針異常。
我們普遍的做法是通過判斷user != null然后獲取名稱
1
2
3
4
5
6
|
public
static
String getUserName(User user){
if
(user !=
null
){
return
user.getName();
}
return
null
;
}
|
但是如果對象嵌套的層次比較深的時候這樣的判斷我們需要編寫多少次呢?難以想象
處理空指針
使用Optional優化代碼
1
2
3
4
|
public
static
String getUserNameByOptional(User user) {
Optional<String> userName = Optional.ofNullable(user).map(User::getName);
return
userName.orElse(
null
);
}
|
當user為null的時候我們設置UserName的值為null,否則返回getName的返回值,但此時不會拋出空指針。
在之前的代碼片段中是我們最熟悉的命令式編程思維,寫下的代碼可以描述程序的執行邏輯,得到什么樣的結果。 后面的這種方式是函數式思維方式,在函數式的思維方式里,結果比過程更重要,不需要關注執行的細節。程序的具體執行由編譯器來決定。 這種情況下提高程序的性能是一個不容易的事情。
我們再次了解下Optional中的一些使用方法
Optional方法
創建 Optional 對象
你可以通過靜態工廠方法Optional.empty,創建一個空的Optional對象:
1
|
Optional<User> emptyUser = Optional.empty();
|
創建一個非空值的Optional
1
|
Optional<User> userOptional = Optional.of(user);
|
如果user是一個null,這段代碼會立即拋出一個NullPointerException,而不是等到你試圖訪問user的屬性值時才返回一個錯誤。
可接受null的Optional
1
|
Optional<User> ofNullOptional = Optional.ofNullable(user);
|
使用靜態工廠方法Optional.ofNullable,你可以創建一個允許null值的Optional對象。
如果user是null,那么得到的Optional對象就是個空對象,但不會讓你導致空指針。
使用map從Optional對象中提取和轉換值
1
2
|
Optional<User> ofNullOptional = Optional.ofNullable(user);
Optional<String> userName = ofNullOptional.map(User::getName);
|
這種操作就像我們之前在操作Stream是一樣的,獲取的只是User中的一個屬性。
默認行為及解引用Optional對象
我們決定采用orElse方法讀取這個變量的值,使用這種方式你還可以定義一個默認值, 遭遇空的Optional變量時,默認值會作為該方法的調用返回值。 Optional類提供了多種方法讀取 Optional實例中的變量值。
- get()是這些方法中最簡單但又最不安全的方法。如果變量存在,它直接返回封裝的變量 值,否則就拋出一個NoSuchElementException異常。所以,除非你非常確定Optional 變量一定包含值,否則使用這個方法是個相當糟糕的主意。此外,這種方式即便相對於 嵌套式的null檢查,也並未體現出多大的改進。
- orElse(T other)是我們在代碼清單10-5中使用的方法,正如之前提到的,它允許你在 Optional對象不包含值時提供一個默認值。
- orElseGet(Supplier<? extends T> other)是orElse方法的延遲調用版,Supplier 方法只有在Optional對象不含值時才執行調用。如果創建默認值是件耗時費力的工作, 你應該考慮采用這種方式(借此提升程序的性能),或者你需要非常確定某個方法僅在 Optional為空時才進行調用,也可以考慮該方式(這種情況有嚴格的限制條件)。
- orElseThrow(Supplier<? extends X> exceptionSupplier)和get方法非常類似, 它們遭遇Optional對象為空時都會拋出一個異常,但是使用orElseThrow你可以定制希 望拋出的異常類型。
- ifPresent(Consumer<? super T>)讓你能在變量值存在時執行一個作為參數傳入的 方法,否則就不進行任何操作。
當前除了這些Optional類也具備一些和Stream類似的API,我們先看看Optional類方法:
用Optional封裝可能為null的值
目前我們寫的大部分Java代碼都會使用返回NULL的方式來表示不存在值,比如Map中通過Key獲取值, 當不存在該值會返回一個null。 但是,正如我們之前介紹的,大多數情況下,你可能希望這些方法能返回一個Optional對象。 你無法修改這些方法的簽名,但是你很容易用Optional對這些方法的返回值進行封裝。
我們接着用Map做例子,假設你有一個Map<String, Object>類型的map,訪問由key的值時, 如果map中沒有與key關聯的值,該次調用就會返回一個null。
1
|
Object value = map.get(
"key"
);
|
使用Optional封裝map的返回值,你可以對這段代碼進行優化。要達到這個目的有兩種方式: 你可以使用笨拙的if-then-else判斷語句,毫無疑問這種方式會增加代碼的復雜度; 或者你可以采用Optional.ofNullable方法
1
|
Optional<Object> value = Optional.ofNullable(map.get(
"key"
));
|
每次你希望安全地對潛在為null的對象進行轉換,將其替換為Optional對象時,都可以考慮使用這種方法。
今天七夕,祝天下有情人終成眷屬!