天天看點

學習Java8中Optional

一、Java8之前通路對象的屬性或方法

Java8之前通路對象的屬性或方法,會使用到以下方式,以下方式可能會造成空指針異常

String code = user.getAddress().getCountry().getCode().toUpperCase();      

在上面的代碼中,如果要確定不發生空指針異常必須使用非空判斷,如下所示

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

代碼變得很冗長,難以維護。并且如果再新增加屬性,将會增加更多的if判斷。為了簡化這個過程,我們來看看用 Optional  類是怎麼做的。從建立和驗證執行個體,到使用其不同的方法,并與其它傳回相同類型的方法相結合,下面是見證 Optional  奇迹的時刻。

二、建立Optional對象

1. 建立包含空值的Optional

Optional.empty();      

Optional.of()/Optional.ofNullable()

你可以使用Optional.of()或ofNullable()方法建立Optional對象,兩個方法的差別是,使用Optional.of()方法建立對象時,當參數為null時,調用get()方法,程式會抛出NullPointerException。如下圖所示

學習Java8中Optional

使用Optional.ofNullable(),當參數為null時,程式會抛出

java.util.NoSuchElementException: No value present

異常。如下圖所示

學習Java8中Optional

三、擷取Optional對象的值

1. get()方法

使用get()方法,此方法在值為null時,抛出異常

@Test
    public void getOptionValue(){
            String name = "John";
        Optional<String> opt = Optional.ofNullable(name);
        System.out.println(opt.get());

    }      

2. isPresent()

判斷是否有值,再進行後續操作

@Test
    public void getOptionValue(){
            String name = "John";
        Optional<String> opt = Optional.ofNullable(name);
        if(opt.isPresent()){
            System.out.println(opt.get());
        }
    }      

3. ifPresent()

判斷是否有值再進行後續操作,接受一個Consumer(消費者) 參數,如果對象不是空的,就對執行傳入的 Lambda 表達式

@Test
    public void getOptionValue(){
            String name = "John";
        Optional<String> opt = Optional.ofNullable(name);
        opt.ifPresent(System.out::println );
    }      

四、傳回預設值

1. orElse()

orElse()方法,它的工作方式非常直接,如果有值則傳回該值,否則傳回傳遞給它的參數值。如下示例代碼,如果name為null則傳回"Sim",如果name不為空則直接傳回name。

@Test
    public void getOptionOrElse(){
        String name = null;
        Optional<String> opt = Optional.ofNullable(name);
        String nameStr= opt.orElse("Sim");
        System.out.println(nameStr);
    }      

2.orElseGet()

此方法略有不同。這個方法會在有值的時候傳回值,如果沒有值,它會執行作為參數傳入的 Supplier(供應者) 函數式接口,并将傳回其執行結果:

@Test
    public void getOptionOrElseGet(){
        User user =null;
        User user2 = new User();
        user2.setName("abc");
        User user3=  Optional.ofNullable(user).orElseGet(
                () -> user2);
        System.out.println(user3);
    }      

3.orElse和orElseGet差別

當Optional中對象為空是,兩者沒有差別都,傳回預設值或供給函數提供的值。

當Optional中對象不為空,orElse仍然執行産生預設值的方法,如下圖所示:

學習Java8中Optional

當Optional中對象不為空,orElseGet不會執行産生預設值的方法,如下圖所示:

學習Java8中Optional

五、傳回異常

1.orElseThrow()

當Optional對象值為空的時抛出異常,異常可以自定義。

@Test
    public void getOptionOrElseThrow(){
        User user2 = null;
        //當user2為空時,抛出異常
        User user3=  Optional.ofNullable(user2).orElseThrow(
                () -> new IllegalArgumentException() );
    }      

六、轉換值

1.map方法

map方法接收一個function接口,并且把傳回值包裝成Optional對象,使對傳回值進行鍊式調用的操作成為可能,如下代碼所示,調用map後,可緊着調用orElse

@Test
    public void testMap(){
        User user2 = new User();
        user2.setName("Lisa");
        user2.setAddress("上海");

        String address =
                Optional.ofNullable(user2).
            map(u -> u.getAddress()).orElse("北京");
        System.out.println(address);
    }      

2.flatMap

map方法接收一個function接口,傳回值直接是一個Optional對象,不需要進行重新包裝。

public Optional<String> getCode(){
        return Optional.ofNullable(code);
    } 

@Test
    public void testFlatMap(){
        User user2 = new User();
        user2.setName("Lisa");
        user2.setAddress("上海");
        String code =
                Optional.ofNullable(user2).flatMap(u -> u.getCode()).orElse("北京");
        System.out.println(code);
    }      

七、過濾值

Optional  類也提供了按條件“過濾”值的方法,filter() 接受一個 Predicate 參數,傳回測試結果為 true 的值。如果測試結果為 false,會傳回一個空的 Optional。

@Test
    public void testFilter() {
        User user = new User();
        user.setEmail("[email protected]");
        Optional<User> result =
                Optional.ofNullable(user)
                .filter(u -> StringUtils.isNotEmpty(u.getEmail()) && u.getEmail().contains("@"));
        System.out.println(result);
    }      

八、怎麼使用Optional

1.使用場景

Optional 主要用作傳回類型。在擷取到這個類型的執行個體後,如果它有值,你可以取得這個值,否則可以進行一些替代行為。

Optional 類有一個非常有用的用例,就是将其與流或其它傳回 Optional 的方法結合,以建構流暢的API。

2.不适合使用場景

Optional 不是 Serializable。是以,它不應該用作類的字段。如果你需要序列化的對象包含 Optional 值,

Jackson 庫

支援把 Optional 當作普通對象。也就是說,Jackson 會把空對象看作 null,而有值的對象則把其值看作對應域的值。這個功能在

jackson-modules-java8

項目中。

它在另一種情況下也并不怎麼有用,就是在将其類型用作方法或建構方法的參數時,這樣做會讓代碼變得複雜,完全沒有必要。比如以下代碼

User user = new User(23, "Lisa", Optional.empty());      

九、總結

Optional主要用來解決空指針異常,結合Stream流及Lambda表達式,使應用程式更加優化及健壯。