Java8新特性系列我們已經介紹了Stream、Lambda表示式、DateTime日期時間處理,最後以“NullPointerException” 的剋星Optional類的講解來收尾。
背景
作為開發人員每天與NullPointerException鬥智鬥勇。每接收到引數或呼叫方法獲得值得判斷一下是否為null。稍不留意,空指標異常就像幽靈一樣出現了。
這篇文章我們來學習Java8是如何通過Optional類來避免空指標異常的。
先來看一下不使用Optional類時,我們為了防止NullPointerException會怎麼處理。
public String getParentName(Person son) {
if (son != null) {
Person parent = son.getParent();
if (parent != null) {
return parent.getUsername();
} else {
return "--";
}
}
return "--";
}複製程式碼
為了防止出現異常,需要不停的判斷物件是否為null。但如果業務邏輯比較複雜,會湧現出大量的ifelse。看似邏輯縝密,但易讀性卻並不高。
為了解決相關問題,在Effective Java中建議若方法返回型別為集合,則通過返回空集合以避免 NullPointerException,真是煞費苦心。
先看一下上面的程式碼使用Optional之後會變成什麼樣子。
public String getParentNameWithOptional(Person son) {
return Optional.ofNullable(son).map(Person::getParent).map(Person::getUsername).orElse("--");
}複製程式碼
對照一下程式碼,看看神不神奇?!
Optional類簡介
java.util.Optional
public final class Optional<T> {}複製程式碼
java.util.Optional
通過原始碼會發現,它並沒有實現java.io.Serializable介面,因此應避免在類屬性中使用,防止意想不到的問題。
除了Optional類之外,還擴充套件了一些常用型別的Optional物件,比如:OptionalDouble、OptionalInt、OptionalLong。用法基本上相似。
下面通過具體的操作和功能來了解Optional類。
建立Optional物件
建立Optional物件有三種方法:empty()、of()、ofNullable(),均為靜態方法。
如果Optional物件沒有值則用empty()方法。
Optional empty = Optional.empty();複製程式碼
如果確定Optional物件的值不為null,則可用of()方法。
Optional stringOptional = Optional.of("Hello 公眾號:程式新視界");複製程式碼
如果不確定Optional物件的值是否為null,則可用ofNullable()。比如上面,不確定Person物件是不否null,就用了ofNullable()方法。當然,也可以直接給該方法傳null。
Optional ofNullOptional = Optional.ofNullable(null);複製程式碼
此時,通過呼叫其isPresent方法可以檢視該Optional中是否值為null。
boolean bool = ofNullOptional.isPresent();
System.out.println(bool);複製程式碼
此時如果直接呼叫get方法獲取值,則會丟擲異常。
ofNullOptional.get();複製程式碼
get獲取Optional中的值
通過get方法可獲取Optional中的值,但如果值為null,則會丟擲異常。
Optional ofNullOptional = Optional.ofNullable(null);
ofNullOptional.get();複製程式碼
異常資訊:
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
...複製程式碼
此時,需要另外一個方法的輔助:isPresent()。該方法可判定Optional中是否有值,如果有則返回true,如果沒有則返回false。
Optional ofNullOptional = Optional.ofNullable(null);
boolean bool = ofNullOptional.isPresent();
if(bool){
ofNullOptional.get();
}複製程式碼
map獲取Optional中的值
對於物件操作,也可以通過map來獲取值,最開始簡化的例子就是如此。
Optional<Person> sonOptional = Optional.ofNullable(son);
System.out.println(sonOptional.map(Person::getUsername));複製程式碼
map方法,如果有值,則對其執行呼叫對映函式得到返回值。如果返回值不為null,則建立包含對映返回值的Optional作為map方法返回值,否則返回空Optional。
flatMap獲取Optional中的值
如果有值,則返回Optional型別返回值,否則返回空Optional。flatMap與map方法類似。但flatMap中的mapper返回值必須是Optional。呼叫結束時,flatMap不會對結果用Optional封裝。
Optional<Person> sonOptional = Optional.ofNullable(son);
sonOptional.flatMap(OptionalTest::getOptionalPerson);複製程式碼
呼叫的是當前類OptionalTest的另外一個方法:
public static Optional<Person> getOptionalPerson(Person person){
return Optional.ofNullable(person);
}複製程式碼
orElse獲取Optional中的值
orElse方法,如果有值就返回,否則返回一個給定的值作為預設值;
Optional.empty().orElse("--");複製程式碼
上面這種情況就會返回“--”。
在此,這種操作與三目運算效果一樣。
str != null ? str : "--"複製程式碼
orElseGet獲取Optional中的值
orElseGet()方法與orElse()方法作用類似,但生成預設值的方式不同。該方法接受一個Supplier extends T>函式式介面引數,用於生成預設值;
Optional.empty().orElseGet(() -> {
String a = "關注";
String b = "公眾號:程式新視界";
return a + b;
});複製程式碼
很顯然,這裡可以處理更多的業務邏輯。
orElseThrow獲取Optional中的值
orElseThrow()方法與get()方法類似,當值為null時呼叫會丟擲NullPointerException異常,但該方法可以指定丟擲的異常型別。
Optional.empty().orElseThrow(()-> new RuntimeException("請先關注公眾號!"));複製程式碼
此時列印異常資訊為:
Optional.empty().orElseThrow(()-> new RuntimeException("請先關注公眾號!"));複製程式碼
判斷並執行操作
ifPresent方法,可對值進行判斷然後列印,接收引數為Consumer super T>函式式介面。
Optional.of("公眾號:程式新視界").ifPresent(System.out::println);複製程式碼
當然,也可以在函式中執行其他複雜操作:
Optional.of("公眾號:程式新視界").ifPresent((val)->{
System.out.println("歡迎關注" + val);
});複製程式碼
filter()方法過濾
filter()方法可用於判斷Optional物件是否滿足給定條件,一般用於條件過濾:
Optional.of("公眾號:程式新視界").filter((val)->{
return val.contains("程式新視界");
});
// 簡化寫法
Optional.of("公眾號:程式新視界").filter((val)-> val.contains("程式新視界"));複製程式碼
使用誤區
關於使用Optional的誤區有以下:
- 正確的使用建立方法,不確定是否為null時儘量選擇ofNullable方法。
- 避免用在成員變數上(原因上面已經提到);
- 避免直接呼叫Optional物件的get和isPresent方法;
最後一條可能難理解,試想一下如果先用isPresent方法獲得是否存在,然後決定是否呼叫get方法和之前的ifelse判斷並無二致。
Java8提倡函數語言程式設計,新增的許多API都可以用函數語言程式設計表示,Optional類也是其中之一。
小結
至此,Java8新特性相關的內容便完結了。
Java8新特性系列相關文章:
- 《JAVA8新特性時間日期庫DATETIME API及示例》
- 《JAVA8 LAMBDA表示式詳解手冊及例項》
- 《JAVA8 STREAM效能如何及評測工具推薦》
- 《JAVA8 STREAM新特性詳解及實戰》
- 《Java8新特性之空指標異常的剋星Optional類》
關注公眾號“程式新視界”,回覆“001”,獲得整個《Java8新特性系列》的PDF版本。
原文連結:《Java8新特性之空指標異常的剋星Optional類》