Java8新特性整理之Optional取代Null引用
Java8新特性整理之Optional取代Null引用
版權宣告:歡迎轉載!請註明出處! https://blog.csdn.net/u011726984/article/details/79315837
java8之前如何避免空指標異常
相信大家在開發中都會碰到NullPointerException 空指標異常導致程式停止的情況。
下面就來談談在java8之前如何避免空指標異常。
一個擁有汽車及汽車保險的客戶。
Person.java
public class Person { private Car car; public Car getCar() { return car; } }
Car.java
public class Car {
private Insurance insurance;
public Insurance getInsurance() { return insurance; }
}
Insurance.java
public class Insurance {
private String name;
public String getName() { return name; }
}
現在我們需要獲取客戶的汽車投保的公司名稱,java8之前我們一般這麼做:
private String getCarInsuranceName(Person person) { if (person != null) { Car car = person.getCar(); if (car != null) { Insurance insurance = car.getInsurance(); if (insurance != null) { return insurance.getName(); } } } return "Unknown"; }
這是糟糕的,不易閱讀的程式碼。每個null檢查都會增加呼叫鏈上剩餘程式碼的巢狀層數。
或者:
private String getCarInsuranceName2(Person person) { if (person == null) { return "Unknown"; } Car car = person.getCar(); if (car == null) { return "Unknown"; } Insurance insurance = car.getInsurance(); if (insurance == null) { return "Unknown"; } return insurance.getName(); }
這會導致每個null檢查都會新增新的退出點。
java8之後如何避免空指標異常
那麼java8中如何優雅的解決上面的問題呢?這就要引入Optional
類。
變數存在時, Optional類只是對類簡單封裝。變數不存在時,缺失的值會被建模成一個“空”的Optional物件,由方法Optional.empty()返回。
null引用和Optional.empty()有什麼本質的區別嗎?
從語義上,你可以把它們當作一回事兒,但是實際中它們之間的差別非常大:如果你嘗試解引用一個null,一定會觸發NullPointerException,不過使用Optional.empty()就完全沒事兒,它是Optional類的一個有效物件,多種場景都能呼叫,非常有用。
使用Optional重新定義Person/Car/Insurance的資料模型
Person.java
public class Person {
private Optional<Car> car;
public Optional<Car> getCar() { return car; }
}
Car.java
public class Car {
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() { return insurance; }
}
Insurance.java
public class Insurance {
private String name;
public String getName() { return name; }
}
現在我們需要獲取客戶的汽車投保的公司名稱,java8之前我們一般這麼做:
public String getCarInsuranceAsOptional(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("UnKnown");
}
解釋下,Optional封裝的Person物件,建立 Optional 物件 有三種方式:
1. 宣告一個空的Optional
Optional<Person> optPerson = Optional.empty();
- 依據一個非空值建立Optional
Optional<Person> optPerson = Optional.of(person);
- 可接受null的Optional
Optional<Person> optPerson = Optional.ofNullable(person);
如果car是null,那麼得到的Optional物件就是個空物件。
使用 flatMap
和 map
從 Optional 物件中提取和轉換值
optPerson是Optional型別的變數, 呼叫map方法應該沒有問題。但getCar返回的是一個Optional型別的物件,這意味著map操作的結果是一個Optional
public class Person {
private Car car;
public Optional<Car> getCarAsOptional() {
return Optional.ofNullable(car);
}
}
Optional類提供了多種方法讀取Optional例項中的變數值:
- get()
是這些方法中最簡單但又最不安全的方法。如果變數存在,它直接返回封裝的變數值,否則就丟擲一個NoSuchElementException異常。所以,除非你非常確定Optional
變數一定包含值,否則使用這個方法是個相當糟糕的主意。此外,這種方式即便相對於巢狀式的null檢查,也並未體現出多大的改進。
- orElse(T other)
,它允許你在Optional物件不包含值時提供一個預設值。
- orElseGet(Supplier<? extends T> other)
是orElse方法的延遲呼叫版, Supplier
方法只有在Optional物件不含值時才執行呼叫。如果建立預設值是件耗時費力的工作,你應該考慮採用這種方式(藉此提升程式的效能),或者你需要非常確定某個方法僅在
Optional為空時才進行呼叫,也可以考慮該方式(這種情況有嚴格的限制條件)。
- orElseThrow(Supplier<? extends X> exceptionSupplier)
和get方法非常類似,
它們遭遇Optional物件為空時都會丟擲一個異常,但是使用orElseThrow你可以定製希望丟擲的異常型別。
- ifPresent(Consumer<? super T>)
讓你能在變數值存在時執行一個作為引數傳入的方法,否則就不進行任何操作。
使用 Optional的filter方法過濾特定條件
需求:你可能需要檢查保險公司的名稱是否為“香港保險”
java8之前需要這麼做:
public void testGetInsuranceName(Car car) {
Insurance insurance = car.getInsurance();
if(insurance != null && "香港保險".equals(insurance.getName())) {
System.out.println("ok");
}
}
java8之後這麼做:
public void testGetInsuranceName2(Car car) {
Optional<Insurance> optInsurance = car.getInsurance();
optInsurance.filter(insurance -> "香港保險".equals(insurance.getName()))
.ifPresent(x -> System.out.println("ok"));
}
filter
方法接受一個謂詞作為引數,如果Optional物件的值存在,並且它符合謂詞的條件,filter方法就返回其值;否則它就返回一個空的Optional物件。
基礎型別的Optional物件,以及為什麼應該避免使用它們
不知道你注意到了沒有,與Stream物件一樣,Optional也提供了類似的基礎型別——OptionalInt
、OptionalLong
以及OptionalDouble
。
之前討論過使用基礎型別Stream的場景,尤其是如果Stream物件包含了大量元素,出於效能的考量,
使用基礎型別是不錯的選擇,但對Optional物件而言,這個理由就不成立了,因為Optional物件最多隻包含一個值。
小結
- null引用在歷史上被引入到程式設計語言中,目的是為了表示變數值的缺失。
- Java 8中引入了一個新的類java.util.Optional,對存在或缺失的變數值進行建模。
- 你可以使用靜態工廠方法Optional.empty、Optional.of以及Optional.ofNullable建立Optional物件。
- Optional類支援多種方法,比如map、flatMap、filter,它們在概念上與Stream類中對應的方法十分相似。
- 使用Optional會迫使你更積極地解引用Optional物件,以應對變數值缺失的問題,最終,你能更有效地防止程式碼中出現不期而至的空指標異常。
- 使用Optional能幫助你設計更好的API,使用者只需要閱讀方法簽名,就能瞭解該方法是否接受一個Optional型別的值。