Java基礎系列-Optional
原創文章,轉載請標註出處:《Java基礎系列-Optional》
一、概述
Optional的引入是為了解決null的問題,那麼到底是解決null的什麼問題呢?
我們知道當我們針對null呼叫方法的之後,就會丟擲空指標異常,Optional就是為了解決這個問題而來的。
Optional通過封裝目標物件的方式來表示,當我們使用的時候,Optional是必然存在的,因為如果結果為null,會返回一個固定的EMPTY例項,這樣就不會存在null引用的問題了。
那該如何來使用Optional呢?
我們不能濫用Optional,只有在邏輯上(業務上)可能為null時才使用Optional來封裝目標物件,如果邏輯上(業務上)肯定不可能為null時,就不要使用Optional封裝物件,這樣當閱讀程式碼的人看到有Optional則表示目標物件是可以為null的。
這樣之後,如果程式中又出現了null指標異常,那麼只能是你的程式碼邏輯有誤,而不可能是非邏輯原因了,也就是不該為null的時候出現了null,這肯定是你邏輯搞錯了。
二、Optional
2.1 建立Optional例項
2.1.1 建立一個空的Optional
public class OptionalTest { public static void main(String[] args) { Optional<String> o = Optional.empty();// 建立一個空Optional } }
2.1.2 建立一個有值的Optional
public class OptionalTest { public static void main(String[] args) { Optional<String> op = Optional.of("123");// 建立一個目標物件必須有值的Optional } }
這種建立方式,如果of的引數為null,則直接丟擲異常。
2.1.3 建立一個可為空的Optional
public class OptionalTest { public static void main(String[] args) { Optional<String> opt = Optional.ofNullable(null);// 建立一個目標物件可為null的Optional } }
2.2 中間操作-處理Optional
2.2.1 filter
這個filter是一個校驗器,由於Optional中也只有一個元素,稱其為過濾有點太大了,如果Optional中的元素滿足給定的校驗條件,則將封裝元素的Optional返回,否則返回空的Optional。
public class OptionalTest { public static void main(String[] args) { filterTest(); } public static void filterTest(){ Optional<String> os = Optional.of("123456").filter(e -> e.length()>7); System.out.println(os.isPresent()); } }
執行結果:
false
因為字串“123456”長度小於7,所以返回一個空的Optional。
2.2.2 map
map是對映之意,就是針對Optional封裝的元素進行操作,然後返回一個封裝著操作結果的新的Optional。
public class OptionalTest { public static void main(String[] args) { mapTest(); } public static void mapTest(){ Optional<Integer> oi = Optional.of("abcdefg").map(e -> e.length()); System.out.println(oi.get()); } }
執行結果:
2.2.3 flatMap
這個方法是扁平化對映,所謂扁平就是處理巢狀的Optional。
比如有一個Person類,其中有一個Optional<String>型別的欄位name,我們在別處獲取到了一個Optional<Person>型別的例項,現在想要獲取到這個Person的的姓名,這就是巢狀的Optional。
class Person{ Optional<String> name; public Person(String name){ this.name = Optional.of(name); } public Optional<String> getName(){ return name; } }
如果我們使用map獲取到的是如下的:
public class OptionalTest { public static void main(String[] args) { flatMapTest(); } public static void flatMapTest(){ Optional<Person> op = Optional.of(new Person("huahua")); Optional<Optional<String>> oos = op.map(Person::getName); String name = oos.get().orElseGet(()->"noName"); System.out.println(name); } }
而我們使用flatMap的話就可以是這樣的:
public class OptionalTest { public static void main(String[] args) { flatMapTest(); } public static void flatMapTest(){ Optional<Person> op = Optional.of(new Person("huahua")); Optional<String> os = op.flatMap(Person::getName); String name = os.orElseGet(()->"noName"); System.out.println(name); } }
巢狀的Optional要獲取到目標物件,必須通過多次去殼才行,這也是使用map對映的原理,但是使用flatMap,扁平化功能,一次去殼操作就完成了。
2.3 從Optional中獲取目標值
有四種方式:
- public T get():直接獲取Optional中的值,如果是空的要丟擲NoSuchElementException異常
- public T orElse(T other):如果Optional不為空,則將值返回,否則返回指定的other(預設值)
- public T orElseGet(Supplier<? extends T> other):如果Optional不為空,則返回值,否則將通過指定方式other生成的值返回
- public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X:如果Optional不為空,返回值,否則丟擲由指定的exceptionSupplier生成的異常
public class OptionalTest { public static void main(String[] args) { getTest(); orElseTest(); } public static void getTest(){ Optional<String> os = Optional.of("123456"); System.out.println(os.get()); } public static void orElseTest(){ Optional<String> os = Optional.empty(); System.out.println(os.orElse("default")); System.out.println(os.orElseGet(()->"default")); System.out.println(os.orElseThrow(RuntimeException::new)); } }
執行結果:
123456 default default Exception in thread "main" java.lang.RuntimeException at java.util.Optional.orElseThrow(Optional.java:290) at com.dh.stream.OptionalTest.orElseTest(OptionalTest.java:29) at com.dh.stream.OptionalTest.main(OptionalTest.java:17)
三、總結
說了這麼多,最終是為了在開發中使用Optional,正如開始時說的,我們要明確Optional規避的是那種null,不能在所有的地方都使用它。
當專案的業務規則下某個物件可能為null(就是業務允許的null),這種情況下,出現null是正常現象,這種情況需要規避,我們使用Optional封裝目標物件,保證不會存在null呼叫丟擲空指標。
但是如果在業務規則下某個物件不可能為null(就是業務不允許為null),這種情況下,出現null就是程式出錯了,並不是正常現象,這種時候我們不能用Optional去封裝目標物件來規避問題,而是要直接使用,一旦出錯就可以及時的排查問題,不至於被Optional將問題給隱藏掉。