Java8-Optional

Java8引入了全新的Optional類,主要用來處理空指針異常(NullPointerException)。從本質(zhì)上說該類屬于包含可選值的封裝類(wrapper class),因此它既可以包含對象也可以僅僅為空。

舉例說明:

在 Java 8 之前,凡涉及到訪問對象方法或者對象屬性的操作,無論數(shù)量多寡,都可能導(dǎo)致空指針異常:

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

為確保上面實例不出現(xiàn)空指針異常,需對每一個值進行顯式的null檢查

if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            if (isocode != null) {
                isocode = isocode.toUpperCase();
            }
        }
    }
}

但是很多時候會忘記null的檢查,即使沒有忘記寫出上面的代碼也將很難維護

為了解決上述問題,JDK在Java8的時候加入了Optional,Optional的javadoc介紹如下

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.

這是一個可以包含或者不包含非 null 值的容器。如果值存在則 isPresent()方法會返回 true,調(diào)用 get() 方法會返回該對象

構(gòu)造Optional

JDK提供了三個靜態(tài)方法來構(gòu)造一個Optional

Optional.of(T value)

該方法通過一個非null的value來構(gòu)造一個Optional,返回的Optional包含了value這個值,對于該方法,傳入的參數(shù)一定不能為null,否則會拋出NullPointerException

Optional.ofNullable(T value)

該方法和of的區(qū)別在于,傳入的參數(shù)可以為null,與之前Optional javadoc提到的不為null有沖突,原因可以參考ofNullable方法的源碼

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

該方法會對傳入的參數(shù)進行null判斷,如果為null,實際上返回的是Optional.empty

Optional.empty()

該方法用來構(gòu)造一個空的Optional,底層代碼實現(xiàn)如下

    /**
     * Common instance for {@code empty()}.
     */
    private static final Optional<?> EMPTY = new Optional<>();

    /**
     * If non-null, the value; if null, indicates no value is present
     */
    private final T value;

    /**
     * Constructs an empty instance.
     *
     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
     * should exist per VM.
     */
    private Optional() {
        this.value = null;
    }

方法使用總結(jié)

前面JavaDoc提到,Optional的isPresent()用來判斷是否包含值,get()方法用來獲取Optional包含的值,需要注意在Optional.empty上調(diào)用get()方法將拋出NoSuchElementException異常

舉例如下,完整代碼在文章底部

Optional<User> user = Optional.ofNullable(getUserById(id));
if (user.isPresent()) {
    String username = user.get().getUsername();
    System.out.println("Username is: " + username); // 使用 username
}

和之前使用null的判斷沒啥區(qū)別,還需要使用Optional封裝value,增加了代碼里,實際上這不是使用Optional的正確姿勢,下面將詳細介紹Optional各個方法的使用

ifPresent

底層源碼

public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

上面獲取用戶名的例子可以重構(gòu)成如下代碼

Optional<User> user = Optional.ofNullable(getUserById(userId));
user.ifPresent(u -> System.out.println(u.getUsername()));

orElse

底層源碼

public T orElse(T other) {
    return value != null ? value : other;
}

使用舉例

User user1 = Optional
    .ofNullable(getUserById(nullUserId))
    .orElse(new User("Unknown", "Unknown", 99));
System.out.println(user1.toString());

orElseGet

orElseGet與orElse的區(qū)別在于,orElseGet方法傳入的參數(shù)為一個Supplier接口的實現(xiàn),當(dāng)Optional有值的時候,返回值,沒有值的時候,返回該Supplier的值

底層源碼

public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

使用舉例

User user2 = Optional
    .ofNullable(getUserById(userId))
    .orElseGet(() -> new User("Unknown", "unknown", 88));
System.out.println(user2.toString());

orElseThrow

orElseThrow與orElse方法的區(qū)別,orElseThrow方法當(dāng)Optional中有值的時候返回值,沒有值的時候拋出異常,拋出的異常由傳入的exceptionSupplier提供

底層源碼

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

使用舉例

try {
    User user3 = Optional
        .ofNullable(getUserById(nullUserId))
        .orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
    } catch (Exception e) {
        e.printStackTrace();
}

map

如果當(dāng)前Optional為Optional.empty,則依舊返回Optional.empty;否則返回一個新的Optional,該Optional包含的是:函數(shù)mapper在以value作為輸入時的輸出值,可以多次使用map操作

底層源碼

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));
    }
}

使用舉例

String userName = Optional.ofNullable(getUserById(nullUserId))
    .map(u -> u.getUsername())
    .map(name -> name.toLowerCase())
    .orElse("Unknown");
System.out.println(userName);

flatMap

flatMap與map方法的區(qū)別在于,map方法參數(shù)中函數(shù)mapper輸出的是值,然后map方法會使用Optional.ofNullable將其包裝為Optional,而flatMap要求參數(shù)中的函數(shù)mapper輸出的就是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));
    }
}

使用舉例

Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
    .flatMap(u -> Optional.of(u.getUsername()))
    .flatMap(name -> Optional.of(name.toLowerCase()));
System.out.println(userName1);

filter

filter方法接收一個Predicate來對Optional中包含的值進行過濾,如果包含的值滿足條件,則還是返回這個Optional,否則返回Optional.empty

底層源碼

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

使用舉例

Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
    .filter(u -> u.getUsername() != "Jack")
    .map(u -> u.getUsername());
System.out.println(userName2);

完整代碼

    public static void main(String[] args) {

        int nullUserId = 0;
        int userId = 666;
        Optional<User> user = Optional.ofNullable(getUserById(userId));
        user.ifPresent(u -> System.out.println(u.getUsername()));

        User tmpUser = new User();
        user.ifPresent(u -> tmpUser.setUsername(u.getUsername()));
        System.out.println(tmpUser.toString());

        user.ifPresent(u -> {int age = u.getAge();
            System.out.println(age);});

        User user1 = Optional
                .ofNullable(getUserById(nullUserId))
                .orElse(new User("Unknown", "Unknown", 99));
        System.out.println(user1.toString());

        User user2 = Optional
                .ofNullable(getUserById(userId))
                .orElseGet(() -> new User("Unknown", "unknown", 88));
        System.out.println(user2.toString());

        try {
            User user3 = Optional
                    .ofNullable(getUserById(nullUserId))
                    .orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
        } catch (Exception e) {
            e.printStackTrace();
        }

        String userName = Optional.ofNullable(getUserById(nullUserId))
                .map(u -> u.getUsername())
                .map(name -> name.toLowerCase())
                .orElse("Unknown");
        System.out.println(userName);

        Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
                .flatMap(u -> Optional.of(u.getUsername()))
                .flatMap(name -> Optional.of(name.toLowerCase()));
        System.out.println(userName1);

        Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
                .filter(u -> u.getUsername() != "Jack")
                .map(u -> u.getUsername());
        System.out.println(userName2);
    }

    private static User getUserById(int userId) {
        if (666 == userId) {
            return new User("Jack", "Beijing", 18);
        } else {
            return null;
        }
    }

參考

使用 Optional 處理 null
了解、接受和利用Java中的Optional

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 關(guān)于java8 Optional 文檔版本:v1.0版本 和C/C++不一樣,java從一開始就嘗試將指針徹底的包...
    比軒閱讀 3,902評論 1 22
  • Optional的學(xué)習(xí)與實戰(zhàn) 整片文章大部分內(nèi)容來自java8實戰(zhàn)這本書,我在這里也是將自己的學(xué)習(xí)過程記錄下來,并...
    Java大寶寶閱讀 3,001評論 2 0
  • 根據(jù)Oracle文檔,Optional是一個容器對象,可以包含也可以不包含非null值。Optional在Java...
    MadPecker閱讀 10,834評論 1 8
  • 厭倦了空指針異常? 考慮使用Java SE 8的Optional!使代碼更具可讀性并使得免受空指針異常的影響。有人...
    bern85閱讀 504評論 0 2
  • Optional java8添加的容器對象,在一些場景下避免使用null檢查而設(shè)定的類,盡可能避免的NullPoi...
    風(fēng)雨兼程_ad4d閱讀 821評論 0 5

友情鏈接更多精彩內(nèi)容