Java8 新特性連載——Optional用法詳解

用過Java的童鞋應該有很深的體會,Java中經常會出現NPE(NullPointException,空指針)異常,如果不處理好是一件非常頭疼的事情,而為了處理NPE需要增加很多代碼來避免,造成代碼汙染,有潔癖的童鞋可能會受不了哇。在Java8之前,這類人就喜歡用Google公司著名的Guava庫的Optional類來處理NPE問題,使我們的Java代碼更乾淨,也許是受到Guava啟發,Java8也引入了Optional,連名字都一模一樣。

一、引子

我們先來看一段代碼:

public String getGender(Cat cat) {
if(null == cat) {
return "未知";
}
if(cat.getGender() == 0) {
return "公的";
}
return "母的";
}

可以看出,為了處理NPE我們寫了很多防禦性的代碼來避免。

二、Optional如何優雅處理NPE

public String getGender(Cat cat) {
return Optional.ofNullable(cat).map(c -> c.getGender() == 0 ? "公的" : "母的").orElse("未知");
}

簡潔、優雅有木有。

三、Optional的實現原理

通過查看Optional源碼我們可以看出,Optional的狗子函數是私有的,因此不能直接通過new 來創建Optional對象,它有三個靜態方法:

public static Optional of(T value) {
return new Optional<>(value);
}

public static Optional ofNullable(T value) {
return value == null ? empty() : of(value);
}

public static Optional empty() {


@SuppressWarnings("unchecked")
Optional t = (Optional) EMPTY;
return t;
}

三個方法都好理解,唯一要多一句嘴的就是of和ofNullable的區別,從實現上可以知道,of表明Optional包含的對象是絕對不允許為空的,如果為空就會爆NPE,而ofNullable則表示允許為空,最終是不是空交給用戶去處理。一般情況下ofNullable可以滿足我們絕大部分使用場景。那潔癖的童鞋該問了,有了ofNullable為啥還要of?按我的理解,我認為of的使用場景在於明確必須不為空的情況下,如果為空一定要爆NPE,例如我們創建對象時可以這麼寫:

Optional.of(new Cat());

這時,我們就必須保證new Cat()不為空,如果為空說明內存溢出了,必須馬上爆出異常通知。

Optional還有幾個方法做一下解釋:

(1)get

public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}

當用Optional.get()時,若包含對象為空,將會拋出NoSuchElementException異常。

(2)isPresent

public boolean isPresent() {
return value != null;
}

該方法只是做了次判空操作,我覺得在實際應用中是沒有什麼價值的,因為它只是把判空操作封裝成一個方法,僅此而已

(3)ifPresent

public void ifPresent(Consumer super T> consumer) {
if (value != null)
consumer.accept(value);
}

該方法接受一個函數式接口Consumer,Consumer與Function類似,只是Function有返回處理結果,而Consumer沒有。由於ifPresent內部已經做過判空操作,因此我們可以直接使用無需擔心NPE問題。

(4)filter

public Optional filter(Predicate super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}

我們可以通過filter方法對內容進行過濾,例如我們可以過濾出貓齡大於5的貓

public static void filterAge(Cat cat)
{
Optional.ofNullable(cat).filter( c -> c.getAge() > 5).ifPresent(c -> System.out.println("The cat age is more than 5."));
}

(5)map

public Optional map(Function super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}

map方法用於映射,即通過某種規則將對象映射為另外一個對象,上面我們寫的getGender就用了map方法。

(6)orElse、orElseGet、orElseThrow

// 該方法允許Optional處理當內容為空時的替代值,或默認值
public T orElse(T other) {
return value != null ? value : other;
}
// 該方法允許通過一個Supplier函數接口返回一個其他值
public T orElseGet(Supplier extends T> other) {
return value != null ? value : other.get();
}
// 該方法是當為空時拋出異常
public T orElseThrow(Supplier extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}

掌握了Optional,我們也可以寫出簡潔優雅的Java代碼。