在 Java 語言開發中,可能大多數程序員遇到最多的異常就是 NullPointException 空指針異常了。這個當初語言的開發者「僅僅由於這樣實現起來更容易」而容許空引用所帶來的代價是很是慘痛的。而咱們開發者不得不使用多重 if 嵌套判斷來規避 NPE 或者經過多個 if 結合 return 語句來終止程序。且看一個例子java
假如須要處理下面的嵌套對象,這是一個用於汽車、汽車保險的客戶。程序員
public class Person { private Car car; public Car getCar() { return car; } } public class Car { private Insurance insurance; public Insurance getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } }
那麼下面的代碼會存在怎樣的問題呢?數據庫
public String getCarInsuranceNames(Person person) { return person.getCar().getInsurance().getName(); }
沒錯,當這我的沒有車 / 他的車沒有上保險時,代碼會拋出 NPE。或者說這我的根本就是 null,也會直接拋出異常。咱們常見的做法就是在每次 get 方法以後,進行 if 判斷,增長代碼的健壯性。但是這樣代碼會顯得十分臃腫。Java 語言的開發者們也在關注着這些問題。所以在 Java8 提供了新的 API:java.util.Optional 用來優雅的處理 null。接下來就請讀者和我一塊兒揭開 Optional 神祕的面紗吧!安全
PS:Optional 類提供的不少 API 結合 Lambda 表達式食用更佳,另外還有不少 API 和 Stream 流中同名 API 的思想基本一致。所以建議讀者先行了解這兩個知識點,能夠在個人博客 Java8新特性 標籤下學習app
聲明:本文首發於博客園,做者:後青春期的Keats;地址:https://www.cnblogs.com/keatsCoder/ 轉載請註明,謝謝!框架
Optional
如今咱們嘗試着重構以前關於 人 車 保險 的代碼性能
public class Person { private Optional<Car> car; public Optional<Car> getCar() { return car; } } public class Car { private Optional<Insurance> insurance; public Optional<Insurance> getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } }
注意:對於保險來講,咱們從邏輯層面限定每一個保險公司都有名稱,若是沒有,那通常是數據出了問題而非代碼的問題,開發者應該着手去尋找爲何數據庫存在名字爲空的保險公司。而不是這裏拋出 NPE,故而咱們不用將 Insurance 的 name 字段使用 Optional 包裹學習
經過上面的代碼,咱們已經將對象由 Optional 所包裹了,那接下來咱們該如何使用它呢?ui
Optional<Object> empty = Optional.empty();
Optional.empty(); 該方法返回一個空對象,
Optional<Car> car = Optional.of(c);
Optional.of(T t); 方法會返回一個 Optional
Optional
爲了不在建立 Optional 對象時,因爲源對象爲空而引起的 NPE,該類還提供了 ofNullable 方法,當參數爲 null 時,返回 Optional.empty()。內部的 API 是這樣的
public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) { return empty(); } else { return Optional.ofNullable(mapper.apply(value)); } }
Optional 類提供 map 方法,接收一個函數式接口 Function 的實現類,若是調用者是空的,則返回 empty(),不然對 Optional 中的對象 value 調用 Function 實現類中的 apply() 方法,再包裝成 Optional 返回。能夠用下面的圖直觀的看到 map 執行的過程:
請注意,在 map 執行完 apply 方法拿到返回值以後,會主動將返回值再次包裹成 Optional 對象。所以咱們若是按照下面的方式改造咱們以前的方法,編譯是沒法經過的:
person.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName);
咱們來分析一下: person.map(Person::getCar) 改造後的 person 類中, getCar 方法返回 Optional
幸運的是,和 Stream 同樣,Optional 也提供了扁平化流的方法 flatMap()。且看源碼
public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) { return empty(); } else { @SuppressWarnings("unchecked") Optional<U> r = (Optional<U>) mapper.apply(value); return Objects.requireNonNull(r); } }
flatMap() 比 map() 方法多了一個執行完後將嵌套 Optional 強轉成 Optional 的操做,避免了流不能繼續使用的尷尬處境。所以,咱們能夠將獲取保險公司名稱的方法改形成下面這樣:
public String getCarInsuranceName(Optional<Person> person) { return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse("Unknown"); }
其中 orElse() 方法表示當最終 Optional 包裹的對象仍是空時,返回的默認值
PS:因爲 Optional 並無實現序列化接口,所以若是你的項目中使用了某些要求序列化的框架,而且在某個類中使用 Optional 包裹了字段。可能會由序列化引起程序故障。
經過 get() 方法獲取變量,若是變量存在就直接獲得該變量,不然拋出一個 throw new NoSuchElementException("No value present"); 異常。通常不建議使用該方法畢竟直接用 get() 方法了,還要整 Optional 這些花裏胡哨的幹啥呢
在對象爲 null 時提供一個默認值
在對象爲 null 經過調用 supplier 提供者接口的實現,返回一個值
在對象爲 null 拋出一個可定製的異常信息,能夠用來拋出項目中的自定義異常,以便全局異常捕獲器抓取及響應數據
當對象不爲 null 時,執行消費者操做。爲 null 時啥也不幹
咱們經常調用某個對象的某個方法去判斷其屬性。爲了安全操做。首先須要對該對象進行非空校驗。例如要檢查保險公司名稱是否爲 Keats,須要這麼寫
if(i != null && "Keats".equals(i.getName())){ System.out.println("yes"); }
如今咱們能夠這麼寫
Optional<Insurance> insurance = Optional.ofNullable(i); insurance.filter(in -> "Keats".equals(in.getName())).ifPresent(in -> System.out.println("yes"));
先看 filter 的源碼
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) { return this; } else { return predicate.test(value) ? this : empty(); } }
首先第一步檢查了謂詞實現非空,第二步判斷 Optional 中的對象若是爲空則返回空 Optional,若是不爲空執行謂詞方法,條件成立則返回該對象。不然返回空 Optional。即僅當 Optional 中對象不爲 null 且符合條件時,返回該對象以後經過 ifPresent() 方法執行接下來的邏輯。很是方便易懂
Optional 還提供了一些基礎類型對象對應的類,如 OptionalInt、OptionalLong 同 Stream 流同樣,採用基本操做類型處理數據,避免了自動拆裝箱帶來的性能損失。但卻犧牲了 map、flatMap、filter 方法。開發中需酌情使用
碼字不易,若是你以爲讀完之後有收穫,不妨點個推薦讓更多的人看到吧!