Java8新特性之空指針異常的剋星Optional類

Java8新特性系列咱們已經介紹了Stream、Lambda表達式、DateTime日期時間處理,最後以「NullPointerException」 的剋星Optional類的講解來收尾。java

背景

做爲開發人員天天與NullPointerException鬥智鬥勇。每接收到參數或調用方法得到值得判斷一下是否爲null。稍不留意,空指針異常就像幽靈同樣出現了。編程

這篇文章咱們來學習Java8是如何經過Optional類來避免空指針異常的。api

先來看一下不使用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。看似邏輯縝密,但易讀性卻並不高。app

爲了解決相關問題,在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 類是一個封裝了Optional值的容器對象,Optional值能夠爲null,若是值存在,調用isPresent()方法返回true,調用get()方法能夠獲取值。 學習

經過源代碼會發現,它並無實現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 函數式接口參數,用於生成默認值;

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 函數式接口。

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新特性系列相關文章:

關注公衆號「程序新視界」,回覆「001」,得到整個《Java8新特性系列》的PDF版本。

原文連接:《Java8新特性之空指針異常的剋星Optional類


程序新視界:精彩和成長都不容錯過

程序新視界-微信公衆號

相關文章
相關標籤/搜索