概述
在Java8之前, 如果需要對一個變量做一次 null 檢查, 通常會像下面這樣寫
T t = service1.query(); if (t != null) { K k = service2.update(t); if (k != null) { U u = service3.save(k); } }
如果業務比較復雜, 可能會像上面那樣, 使用 if 語句進行多層嵌套, 后期難以擴展。
在Java8中新引入了一個 Optional 類, Optional 類會對可能為 null 值的變量進行建模, 這樣你就不必直接將 null 賦值給變量, 也就不必像上面那樣進行多層嵌套檢查。
在正式介紹 Optional 類的 API之前, 先引入一些實體類, 后面的代碼示例會經常用到它們。
@Data public class Person { private Car car; private int age; public Optional<Car> getCar() { return Optional.ofNullable(car); } } @Data public class Car { private Insurance insurance; public Optional<Insurance> getInsurance() { return Optional.ofNullable(insurance); } } @Data public class Insurance { private String name; }
創建Optional對象
Optional 類提供了三種方式來創建 Optional 對象
- empty(): 創建一個空的 Optional 對象
- of(): 將指定值用 Optional 封裝之后返回,如果該值為 null ,則拋出一個 NullPointerException 異常
- ofNullable(): 將指定值用 Optional 封裝之后返回,如果該值為 null ,則返回一個空的 Optional 對象
@Test public void test1() { //聲明一個空的optional Optional<Car> c1 = Optional.empty(); //根據一個非空的值創建optional, 如果傳入的值是null,會拋出空指針異常 Optional<Car> c2 = Optional.of(new Car()); //可接受null的Optional Optional<Object> c3 = Optional.ofNullable(null); }
flatMap和map用法
flatMap()和map()都是在指定值存在時, 就對該值執行提供的 mapping 函數調用, 返回一個 Optional 類型的值, 否則就返回一個空的 Optional 對象。
注意 flatMap() 方法的轉換函數是 Function<? super T, Optional<U>> mapper , 這個特性和前面介紹的 Stream 類的 flatMap() 很像。
查看一下 Optional 類的源碼, flatMap() 會將 Optional<T> 中的 T 轉換成 Optional<U>, 再將轉換后的 Optional<U> 直接作為方法返回值返回。
@Test public void test2() { Optional<Person> person = Optional.ofNullable(new Person()); String name = person.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("哪吒之魔童降世"); System.out.println(name); //result: 哪吒之魔童降世 }
上面的代碼示例中, 因為 Person 實體類中 car 屬性是一個 null 值, 所以 person.flatMap(Person::getCar) 這個操作會得到一個空的Optional 對象。
這樣就導致了后面的 flatMap() 和 map() 兩次轉換都返回一個空的 Optional 對象。orElse() 方法的含義是如果有值則將其返回,否則返回一個默認值。所以最后的輸出結果是默認值 哪吒之魔童降世 。
在實際業務中, 你可以像下面這樣將 Optional 作為方法入參和返回值使用
@Test public void test3() { Optional<Insurance> insurance1 = find(Optional.of(new Person()), Optional.empty()); System.out.println(insurance1);//result: Optional.empty Optional<Insurance> insurance2 = find(Optional.of(new Person()), Optional.of(new Car())); System.out.println(insurance2);//result: Optional[Insurance(name=null)] } public Optional<Insurance> find(Optional<Person> person, Optional<Car> car) { return person.flatMap(p -> car.map(c -> find(p, c))); } private Insurance find(Person p, Car c) { return new Insurance(); }
filter和ifPresent
- filter(): 如果值存在並且滿足提供的謂詞, 就返回包含該值的 Optional 對象; 否則返回一個空的 Optional 對象
- ifPresent(): 如果值存在, 就執行使用該值的方法調用, 否則什么也不做
@Test public void test4() { Optional.of("攀登者").filter(i -> i.length() < 4).ifPresent(System.out::println);//打印: 攀登者 Optional.<String>empty().filter(i -> i.length() < 4).ifPresent(System.out::println);//沒有輸出 }
下面是一個測試示例, 到本地去運行看下輸出結果, 它可以幫你重新理解一下前面介紹的幾個 API 的用法。
@Test public void test5() { System.out.println(getCarInsuranceName(Optional.empty(), 8));//result: 命運之夜 Person p = new Person(); p.setAge(10); System.out.println(getCarInsuranceName(Optional.of(p), 8));//result: 命運之夜 Insurance insurance = new Insurance(); insurance.setName("知否知否"); Car car = new Car(); car.setInsurance(insurance); p.setCar(car); System.out.println(getCarInsuranceName(Optional.of(p), 8));//result: 知否知否 } //找出年齡大於或者等於 minAge 參數的 Person 所對應的保險公司列表。 public String getCarInsuranceName(Optional<Person> person, int minAge) { return person.filter(p -> p.getAge() >= minAge).flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("命運之夜"); }
Optional與異常的對比
通常在由於某種因素下, 函數無法正確返回某個值, 常見的做法就是使用 try/catch 語句處理返回一個null值, 或者不做任何處理直接拋出一個異常。
如果函數無法正確返回某個值, 且你不需要它拋出異常, 而是要它返回一個默認值, 那么 Optional 可以幫您更優雅的實現
@Test public void test6() { String value = "test"; Integer res = Optional.of(value).flatMap(this::str2Int).filter(i -> i > 0).orElse(0); System.out.println(res);//result: 0 } private Optional<Integer> str2Int(String str) { try { //如果能正確解析, 就將其封裝在 Optional 中返回 return Optional.of(Integer.valueOf(str)); } catch (NumberFormatException ex) { //如果解析發生異常, 就返回一個空的 Optional return Optional.empty(); } }
上面的代碼清單, Integer.valueOf() 會在入參不是一個整型數值時, 拋出 NumberFormatException , 我們這里對它做了處理, 當解析發生異常時, 就返回一個空的 Optional 對象。
上面的用例只有在 value 變量是大於零的整型數值時, 才會輸出 value 變量的值, 否則, 輸出結果都是默認值 0。
總結
最后, 來一張大圖

參考資料
作者:張小凡
出處:https://www.cnblogs.com/qingshanli/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。
