《java 8 實戰》讀書筆記 -第十章 用Optional取代null

1、Optional 類入門

Java 8中引入了一個新的類java.util.Optional<T>。變量存在時,Optional類只是對類簡單封裝。變量不存在時,缺失的值會被建模成一個「空」
的Optional對象,由方法Optional.empty()返回。java

2、應用 Optional 的幾種模式

1.建立 Optional 對象

(1) 聲明一個空的Optional

正如前文已經提到,你能夠經過靜態工廠方法Optional.empty,建立一個空的Optional對象:安全

Optional<Car> optCar = Optional.empty();

(2) 依據一個非空值建立Optional

你還可使用靜態工廠方法Optional.of,依據一個非空值建立一個Optional對象:函數

Optional<Car> optCar = Optional.of(car);

若是car是一個null,這段代碼會當即拋出一個NullPointerException,而不是等到你試圖訪問car的屬性值時才返回一個錯誤。性能

(3) 可接受null的Optional

最後,使用靜態工廠方法Optional.ofNullable,你能夠建立一個容許null值的Optional對象:spa

Optional<Car> optCar = Optional.ofNullable(car);

若是car是null,那麼獲得的Optional對象就是個空對象。設計

Optional提供了一個 get方法用於獲取Optional變量中的值,不過get方法在遭遇到空的Optional對象時也會拋出異常,因此不按照約定的方式使用它,又會讓咱們再度陷入由null引發的代碼維護的夢魘。

2.使用 map 從 Optional 對象中提取和轉換值

好比,你可能想要從insurance公司對象中提取公司的名稱。提取名稱以前,你須要檢查insurance對象是否爲null,代碼以下所示:code

String name = null; 
if(insurance != null){ 
 name = insurance.getName(); 
}

用Optional實現:對象

Optional<Insurance> optInsurance = Optional.ofNullable(insurance); 
Optional<String> name = optInsurance.map(Insurance::getName);

原理示意:
圖片描述接口

3.使用 flatMap 連接 Optional 對象

Optional<Person> optPerson = Optional.of(person); 
Optional<String> name = 
 optPerson.map(Person::getCar) //編譯沒法經過
 .map(Car::getInsurance) 
 .map(Insurance::getName);

不幸的是,這段代碼沒法經過編譯。爲何呢?optPerson是Optional<Person>類型的變量, 調用map方法應該沒有問題。但getCar返回的是一個Optional<Car>類型的對象,這意味着map操做的結果是一個Optional<Optional<Car>>類型的對象。所以,它對getInsurance的調用是非法的,由於最外層的optional對象包含了另外一個optional對象的值,而它固然不會支持getInsurance方法。
正確作法:圖片

public String getCarInsuranceName(Optional<Person> person) { 
 return person.flatMap(Person::getCar) 
 .flatMap(Car::getInsurance) 
 .map(Insurance::getName) //Insurance::getName返回的是String類型,不是Optional
//返回的Optional多是兩種狀況:若是調用鏈上的任何一個
//方法返回一個空的Optional,那麼結果就爲空,不然返回的值就是你指望的保險公司的名稱。
 .orElse("Unknown"); 
}

圖片描述

在域模型中使用Optional,以及爲何它們沒法序列化
因爲Optional類設計時就沒特別考慮將其做爲類的字段使用,因此它也並未實現Serializable接口。
若是你必定要實現序列化的域模型,做爲替代方案,咱們建議你像下面這個例子那樣,提供一個能訪問聲明爲Optional、變量值可能缺失的接口,代碼清單以下:

public class Person { 
 private Car car; 
 public Optional<Car> getCarAsOptional() { 
 return Optional.ofNullable(car); 
 } 
}

4. 默認行爲及解引用 Optional 對象

Optional類提供了多種方法讀取Optional實例中的變量值。

  • get()是這些方法中最簡單但又最不安全的方法。若是變量存在,它直接返回封裝的變量值,不然就拋出一個NoSuchElementException異常。
  • 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>)讓你能在變量值存在時執行一個做爲參數傳入的方法,不然就不進行任何操做

5.以不解包的方式組合兩個Optional對象

public Insurance findCheapestInsurance(Person person, Car car) { 
 // 不一樣的保險公司提供的查詢服務
 // 對比全部數據
 return cheapestCompany; 
}
public Optional<Insurance> nullSafeFindCheapestInsurance( 
 Optional<Person> person, Optional<Car> car) { 
 return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c))); 
}

這段代碼中,你對第一個Optional對象調用flatMap方法,若是它是個空值,傳遞給它的Lambda表達式不會執行,此次調用會直接返回一個空的Optional對象。反之,若是person對象存在,此次調用就會將其做爲函數Function的輸入,並按照與flatMap方法的約定返回一個Optional<Insurance>對象。這個函數的函數體會對第二個Optional對象執行map操做,若是第二個對象不包含car,函數Function就返回一個空的Optional對象,整個nullSafeFindCheapestInsuranc方法的返回值也是一個空的Optional對象。最後,若是person和car對象都存在,做爲參數傳遞給map方法的Lambda表達式可以使用這兩個值安全地調用原始的findCheapestInsurance方法,完成指望的操做。

6.使用 filter 剔除特定的值

Optional<Insurance> optInsurance = ...; 
optInsurance.filter(insurance -> 
 "CambridgeInsurance".equals(insurance.getName())) 
 .ifPresent(x -> System.out.println("ok"));

filter方法接受一個謂詞做爲參數。若是Optional對象的值存在,而且它符合謂詞的條件,
filter方法就返回其值;不然它就返回一個空的Optional對象。
Optional類的方法:
圖片描述
圖片描述

3、使用 Optional 的實戰示例

1.基礎類型的Optional對象

與 Stream對象同樣,Optional也提供了相似的基礎類型——OptionalInt、OptionalLong以及OptionalDouble,若是Stream對象包含了大量元素,出於性能的考量,使用基礎類型是不錯的選擇,但對Optional對象而言,這個理由就不成立了,由於Optional對象最多隻包含一個值。咱們不推薦你們使用基礎類型的Optional,由於基礎類型的Optional不支持map、flatMap以及filter方法,而這些倒是Optional類最有用的方法。

相關文章
相關標籤/搜索