1. 程式人生 > >如何更好地使用Java 8的Optional

如何更好地使用Java 8的Optional

入參 ets address cep lse exceptio else arraylist 一點

Java 8中的Optional<T>

是一個可以包含或不可以包含非空值的容器對象,在 Stream API中很多地方也都使用到了Optional。

  java中非常討厭的一點就是nullpoint,碰到空指針就會出錯拋Exception,然後需要逐行檢查是哪個對象為空,帶來大量的不必要精力損耗,拋出NPE錯誤不是用戶操作的錯誤,而是開發人員的錯誤,應該被避免,那麽只能在每個方法中加入非空檢查,閱讀性和維護性都比較差。

  如下面這個代碼的手工非空檢查:

public void addAddressToCustomer(Customer customer, Address newAddress){
if ( customer == null || newAddress == null) return; if ( customer.getAddresses() == null ){ customer.setAddresses ( new ArrayList&lt;&gt;()); } customer.addAddress(newAddress); }

  另外還有一些開發人員喜歡通過非空檢查來實現業務邏輯,空對象不應該用來決定系統的行為,它們是意外的Exceptional值,應當被看成是錯誤,而不是業務邏輯狀態。

  當我們一個方法返回List集合時,應該總是返回一個空的List,而不是Null,這就允許調用者能夠遍歷它而不必檢查Null,否則就拋出NPE。

  但是如果我們根據標識鍵ID查詢數據庫,沒有查到,需要返回一個空對象怎麽辦?有人建議拋出Exception,其實這不符合函數方法一進一出的原則,變成一個函數方法有兩個返回,一個是正常返回,一個出錯Exception,函數式編程範式告誡我們不要輕易拋Exception。

  這時Java 8的Optional就發揮作用了,允許我們返回一個空的對象。

  Optional<T>有方法 isPresent() 和 get() 是用來檢查其包含的對象是否為空或不是,然後返回它,如:

Optional<SomeType> someValue = someMethod();

if (someValue.isPresent()) { // check

someValue.get().someOtherMethod(); // retrieve and call

}

  但是這種用法並不能體現Java 8的全部好處,你可以將Optional看成是需要使用某個T值的方法之間某種中間人或者協調者Mediator,而不只是一個普通對象的包裝器。

  如果你有一個值返回類型T,你有一個方法需要使用這個值,那麽你可以讓 Optional<T> 處於中間,確保它們之間交互進行,而不必要人工幹預。

  這樣,協調者Optional<T>能夠照顧T的值提供給你的方法作為輸入參數,在這種情況下,如果T是空,可以確保不會出錯,這樣在T值為空時也可以讓一切都正常運作,你也可以讓Optional<T>執行其他動作,如執行一段代碼塊等等,這樣它就實際上是語言機制的很好的補充。

  下面這個案例涉及到Lambda表達式 方法引用,是將單詞流中第一個以"L"開始單詞取出,作為返回結果是一個Optional<String>。

使用ifPresent()

  這個案例的代碼如下:

Stream<string> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<string> longest = names

.filter(name -> name.startsWith("L"))

.findFirst();

longest.ifPresent(name -> {

String s = name.toUpperCase();

System.out.println("The longest name is "+ s);

});

  這裏ifPresent() 是將一個Lambda表達式作為輸入,T值如果不為空將傳入這個lambda。那麽這個lambda將不為空的單詞轉為大寫輸出顯示。在前面names單詞流尋找結果中,有可能找不到開始字母為L的單詞,返回為空,也可能找到不為空,這兩種情況都傳入lambda中,無需我們打開盒子自己編寫代碼來判斷,它自動幫助我們完成了,無需人工幹預。

使用map()

  如果你想從Optional<T>中返回一個值怎麽辦?使用 map(),如下:

Stream<string> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<string> longest = names

.filter(name -> name.startsWith("L"))

.findFirst();

Optional<string> lNameInCaps = longest.map(String::toUpperCase);

  使用Optional<T>的map方法能夠返回另外一個Optional,如上面的 LnameInCaps,因為傳入map()的參數值也許會導致一個空值。

使用orElse()

  如果在T可能空時你需要一個值的話,那麽可以使用 orElse(),它能在T值存在的情況下返回這個值,否則返回輸入值。

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

String alternate = longest.orElse("Nimrod");

System.out.println(alternate); //prints out "Nimrod"

使用orElseGet()

  orElseGet() 方法類似於orElse(),但是不是直接返回輸入參數,而是調用輸入參數,返回調用的結果,這個輸入參數通常是lambda:

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

String alternate = longest.orElseGet(() -> {

// perform some interesting code operation

// then return the alternate value.

return "Nimrod";

});

System.out.println(alternate);

使用 orElseThrow()

  orElseThrow()是在當遭遇Null時,決定拋出哪個Exception時使用:

Stream<String> names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");

Optional<String> longest = names

.filter(name -> name.startsWith("Q"))

.findFirst();

longest.orElseThrow(NoSuchElementStartingWithQException::new);

總結,你能創建下面三種類型的Optional<T>:

Optional<SomeType> getSomeValue() {

// 返回一個空的Optional類型;

return Optional.empty();

}

Optional<SomeType> getSomeValue() {

SomeType value = ...;

// 使用這個方法,值不可以為空,否則拋exception

return Optional.of(value);

}

Optional<SomeType> getSomeValue() {

SomeType value = ...;

// 使用這個方法,值可以為空,如果為空返回Optional.empty

return Optional.ofNullable(value);

// usage

Optional<SomeType> someType = getSomeValue();

如何更好地使用Java 8的Optional