Java8系列 (五) Optional類

概述

在Java8以前, 若是須要對一個變量作一次 null 檢查, 一般會像下面這樣寫app

    T t = service1.query();
    if (t != null) {
        K k = service2.update(t);
        if (k != null) {
            U u = service3.save(k);
        }
    }

若是業務比較複雜, 可能會像上面那樣, 使用 if 語句進行多層嵌套, 後期難以擴展。ide

在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;
}
View Code

建立Optional對象

Optional 類提供了三種方式來建立 Optional 對象this

  • 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 對象。spa

注意 flatMap() 方法的轉換函數是 Function<? super T, Optional<U>> mapper , 這個特性和前面介紹的 Stream 類的 flatMap() 很像。指針

查看一下 Optional 類的源碼, flatMap() 會將 Optional<T> 中的 T 轉換成 Optional<U>, 再將轉換後的 Optional<U> 直接做爲方法返回值返回。code

    @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 對象。orm

這樣就致使了後面的 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。

總結

最後, 來一張大圖

參考資料

Java8 實戰

做者:張小凡
出處:https://www.cnblogs.com/qingshanli/ 本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。若是以爲還有幫助的話,能夠點一下右下角的【推薦】。

相關文章
相關標籤/搜索