原創文章,轉載請標註出處:https://www.cnblogs.com/V1haoge/p/10755368.html
一、概述
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());
}
}
執行結果:
7
2.2.3 flatMap
這個方法是扁平化對映,所謂扁平就是處理巢狀的Optional。
比如有一個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
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使用時不能直接get,這時會走進老路,get的時候可能為null,就會丟擲異常,這時候你就會想要在get之前進行isPresent判斷,這個不使用Optional又有什麼區別的。
我們使用Optional就是為了簡化null判斷,所以拒絕使用get方法,Optional提供了正確使用的方法是:orElse、orElseGet、orElseThrow三個方法。
使用orElse方法,我們可以從非空的Optional中獲取到值,如果是空的Optional,可以返回orElse方法引數指定的預設值。
使用orElseGet方法,我們可以在空的Optional的情況下主動構建一個預設返回結果。
orElseThrow方法,在空Optional的情況下會丟擲一個指定的異常。
四、總結
說了這麼多,最終是為了在開發中使用Optional,正如開始時說的,我們要明確Optional規避的是那種null,不能在所有的地方都使用它。
當專案的業務規則下某個物件可能為null(就是業務允許的null),這種情況下,出現null是正常現象,這種情況需要規避,我們使用Optional封裝目標物件,保證不會存在null呼叫丟擲空指標。
但是如果在業務規則下某個物件不可能為null(就是業務不允許為null),這種情況下,出現null就是程式出錯了,並不是正常現象,這種時候我們不能用Optional去封裝目標物件來規避問題,而是要直接使用,一旦出錯就可以及時的排查問題,不至於被Optional將問題給隱藏掉。