了不起的Java-Optional替代null處理


Optional 類主要解決的問題是臭名昭著的空指針異常(NullPointerException)。是一個包含有可選值的包裝類,這意味着 Optional 類既可以含有對象也可以為空。
在這段代碼就可能產生空異常;

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
//需要檢查:
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();
            }
        }
    }
}

Optional類的依賴依然還是函數接口那一套東西:

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

 

是要把面向接口編程走到底了。私有字段只有一個:

private final T value;

原生代碼還是相當簡單的:

    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

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

    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<Insurance> emptyOpt = Optional.empty();
        try {
            Insurance obj = emptyOpt.get();
            System.out.println("不為空");
        } catch (NoSuchElementException ex) {
            System.out.println("為空");
        }

嘗試訪問 emptyOpt 變量的值會導致 NoSuchElementException,因為原聲代碼:

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

建可能為空(ofNullable不拋異常)

Optional<Insurance> opt = Optional.ofNullable(null);

不會異常
如此神奇,原因在於原生代碼已經進行了判空,把私有value定成了empty(),這種不拋異常也會產生一個問題,那就是在消費實例的時候,還是要過濾掉空值。

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

建為空時拋異常(即of不可為空)

    public static void whenCreateOfEmptyOptional_thenNullPointerException() {        
        Insurance ins = new Insurance();        
        Optional<Insurance> opt = Optional.of(ins);
        ins=null;
        try {
            opt = Optional.of(ins);
            System.out.println("不為空");
        } catch (NullPointerException ex) {
            System.out.println("為空");
        }        
    }

會進入異常。跟蹤原生代碼發現了原因:

    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

訪問 Optional 對象的值

get

從 Optional 實例中取回實際值對象的方法之一是使用 get() 方法:

@Test
public void whenCreateOfNullableOptional_thenOk() {
String name = "John";
Optional<String> opt = Optional.ofNullable(name);

assertEquals("John", opt.get());
}

這個方法會在值為 null 的時候拋出異常。要避免異常,你可以選擇首先驗證是否有值:

@Test
public void whenCheckIfPresent_thenOk() {
User user = new User("john@gmail.com", "1234");
Optional<User> opt = Optional.ofNullable(user);
assertTrue(opt.isPresent());

assertEquals(user.getEmail(), opt.get().getEmail());
}

ifPresent的使用

原生代碼:

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

只有當值不為空,才執行消費者方法accept,這是比較安全的。

    public static void printName(Insurance obj)
    {
        Optional.ofNullable(obj).ifPresent(u ->  System.out.println("The name is : " + u.getName()));
    }

        Insurance obj = new Insurance();
        obj.setName("張三");
        Insurance objNull = null;        
        printName(obj);
        printName(objNull);

//輸出:The name is : 張三

isPresent的使用

感覺有了ifPresent,不需要再用isPresent來判斷空了,否則感覺非常啰嗦。

orElse的使用

在對象為空的時候返回默認值。它的工作方式非常直接,如果有值則返回該值,否則返回傳遞給它的參數值。

Insurance obj = new Insurance();
obj.setName("張三");
Insurance objNull = null;    
Insurance result = Optional.ofNullable(objNull).orElse(obj);
System.out.println(obj.hashCode());
System.out.println(result.hashCode());

result實際是返回的obj,而不是null。

filter的使用

filter()方法接受參數為Predicate對象,用於對Optional對象進行過濾,如果符合Predicate的條件,返回Optional對象本身,否則返回一個空的Optional對象

public static void filterAge(Student student)
{
    Optional.ofNullable(student).filter( u -> u.getAge() > 18).ifPresent(u ->     System.out.println("The student age is more than 18."));
}

map的使用

map()方法的參數為Function(函數式接口)對象,map()方法將Optional中的包裝對象用Function函數進行運算,並包裝成新的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));
        }
    }

下面代碼中,先用ofNullable()方法構造一個Optional<Student>對象,然后用map()計算學生的年齡,返回Optional<Integer>對象(如果student為null, 返回map()方法返回一個空的Optinal對象)

public static Optional<Integer> getAge(Student student)
{
    return Optional.ofNullable(student).map(u -> u.getAge()); 
}

flatMap的使用

跟map()方法不同的是,入參Function函數的返回值類型為Optional<U>類型,而不是U類型,這樣flatMap()能將一個二維的Optional對象映射成一個一維的對象,
總而言之,map進去是什么,出來維度不變,flatMap是為了拍扁結果。

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> nonEmptyGender = Optional.of("male");
        Optional<String> emptyGender = Optional.empty();

        System.out.println("Non-Empty Optional:: " + nonEmptyGender.map(String::toUpperCase));
        System.out.println("Empty Optional    :: " + emptyGender.map(String::toUpperCase));

        Optional<Optional<String>> nonEmptyOtionalGender = Optional.of(Optional.of("male"));
        System.out.println("Optional value   :: " + nonEmptyOtionalGender);
        System.out.println("Optional.map     :: " + nonEmptyOtionalGender.map(gender -> gender.map(String::toUpperCase)));
        System.out.println("Optional.flatMap :: " + nonEmptyOtionalGender.flatMap(gender -> gender.map(String::toUpperCase)));

封裝可能的空值

使用ofNullable,比如在一些map中的key為空的情況下使用:

Optional<Insurance> opt = Optional.ofNullable(map.get("someNullKey"));

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM