玩轉 Java8 Optional,讓你代碼更緊湊簡潔且再也不出現空指針

聆聽 沉澱 傳播 … 關注微信公衆號【Java之言】,助你放棄編程之路!java

系列文章目錄

標題 文章連接
玩轉 Java8 Stream,讓你代碼更高效緊湊簡潔 juejin.cn/post/692113…

1、Optional 是什麼?

官方定義:編程

public final class Optional<T> extends Object微信

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.markdown

Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).app

This is a value-based class; use of identity-sensitivemin operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.ide

Optional 類,一個可包含或不包含非null值容器對象。若是值存在,isPresent() 方法將返回 true 而且 get() 返回該值。函數式編程

這個類還提供了其餘方法,這些方法取決於它所包含值的存在與否,例如 orElse() (若是不存在值,則返回默認值)和 ifPresent()(若是存在值,則執行代碼塊)。函數

這是一個基於值的類,因此對於Optional實例的一些身份敏感操做(包括引用相等==,hashCode,同步)會產生沒法預料的結果,應該避免使用。post

2、Optional 有什麼用?

  1. 既然 Optional 類裏面包含的值能夠爲 null ,不言而喻,那咱們就能夠不用顯式地進行空值檢測,即咱們能夠不用作判空操做,很好的解決了空指針異常問題(NullPointerException)。
  2. Java8引入了函數式編程,而 Optional 是其中必不可少的一部分,幫助在範式中實現。結合 Stream 能夠寫出簡潔高效的代碼。

先舉個栗子,輸出一個員工的崗位名稱,若是崗位名稱爲空,則輸出默認值 - 實習生:ui

if (null != staff.getPost().getName()) {
    System.out.println(staff.getPost().getName());
} else {
    System.out.println("實習生");
}
複製代碼

以上語句有可能拋出NPE,爲了健壯性,咱們須要寫判空:

if (null != staff) {
    Post post = staff.getPost();
    if (null != post) {
        String name = post.getName();
        if (null != name) {
            System.out.println(name);
        } else {
            System.out.println("實習生");
        }
    }
}
複製代碼

可是寫了大量的判空操做,很繁瑣,代碼冗餘還很難維護。若是使用Optional則很簡單以下:

System.out.println(
        Optional.ofNullable(staff).map(Staff::getPost).map(Post::getName).orElse("實習生"));
複製代碼

3、Optional 源碼詳解

public final class Optional<T> {
    
    // 包含一個空值的Optional容器對象
    private static final Optional<?> EMPTY = new Optional<>();

    // Optional容器包含的值
    private final T value;

    // 私有構造方法,建立一個包含空值的Optional容器對象
    private Optional() {
        this.value = null;
    }

    // 靜態方法,建立一個空值的Optional容器對象
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    // 私有構造方法,建立一個包含非空值的Optional容器對象
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    // 靜態方法,建立一個包含非空值的Optional容器對象
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    // 靜態方法,建立一個包含可空值的Optional容器對象
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    // 獲取值,若是值爲null,則拋出異常
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    // 判斷值是否不爲null
    public boolean isPresent() {
        return value != null;
    }

    // 若是值不爲null,則使用改值調用consumer函數
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    // 若是值存志,而且這個值匹配給定的 predicate,返回一個Optional用以描述這個值,不然返回一個空的Optional。
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    // 若是值存在,則對其執行調用映射函數獲得新的返回值。不然返回空Optional。
    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

    // 若是值存在,返回基於Optional包含的映射方法的值,不然返回一個空的Optional
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    // 若是值存在,返回此值, 不然返回other。
    public T orElse(T other) {
        return value != null ? value : other;
    }

    // 若是值存在,返回此值, 不然調用other函數,並返回other函數的結果。
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    // 若是值存在,返回此值,不然拋出由Supplier繼承的異常
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
}
複製代碼

4、Optional 類的使用

4.1 Optional 對象建立

// 建立一個空值的Optional對象
Optional<Object> emptyOptional = Optional.empty();
// 建立一個值可爲空的Optional對象
Optional<Staff> staffOptional = Optional.ofNullable(staff);
// 建立一個非空值的Optional對象
Optional<Staff> staffOptional1 = Optional.of(staff);
複製代碼

4.2 get()

// 返回Optional容器對象中包含的值,若是值爲null則拋出異常
Staff staff = staffOptional.get();
複製代碼

4.3 isPresent()

// 判斷值是否存在
if (staffOptional.isPresent()) {
    Staff staff = staffOptional.get();
}
複製代碼

4.4 ifPresent(Consumer<? super T> consumer)

// 若是值存在,則輸出值
staffOptional.ifPresent(System.out::println);
複製代碼

4.5 filter(Predicate<? super T> predicate)

// 是否存在18歲的員工,若是存在,則輸出她的姓名
staffOptional.filter(staff -> staff.getAge() == 18)
                .ifPresent(staff -> System.out.println(staff.getName()));
複製代碼

4.6 map(Function<? super T, ? extends U> mapper)

// 獲取員工的姓名,若是staff或name爲null,輸出'未知'
System.out.println(staffOptional.map(Staff::getName).orElse("未知"));
複製代碼

4.7 orElse(T other)

// 若是staff爲null,將新生成的Staff對象賦值給staff
staff = Optional.ofNullable(staff).orElse(new Staff());
複製代碼

4.7 orElseGet(Supplier<? extends T> other)

// // 若是staff爲null,才new出一個Staff對象,賦值給staff,它與orElse不一樣的是執行時機不一樣,orElseGet是值爲空才調用other函數。
staff = Optional.ofNullable(staff).orElseGet(Staff::new);
複製代碼

4.8 orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

// 若是staff爲null,則拋出異常
staff = Optional.ofNullable(staff).orElseThrow(() -> new NullPointerException());
複製代碼

4.9 與 Stream 結合使用

// 找出一個員工大於等於18歲的員工
Optional<Staff> staffOptional = staffList.stream().filter(staff -> staff.getAge() >= 18).findFirst();
if (staffOptional.isPresent()) {
    Staff staff = staffOptional2.get();
}
複製代碼
相關文章
相關標籤/搜索