首頁 > Java > java教程 > 主體

Java8 新特性之 Optional 類

黄舟
發布: 2017-02-23 10:30:32
原創
1896 人瀏覽過

摘要: Optional不是對null關鍵字的替代,而是對於null判定提供了一種更優雅的實作

NullPointException可以說是所有java程式設計師都遇到的異常,雖然java從設計之初就試圖讓程式設計師脫離指標的苦海,但是指標確實是實際存在的,而java設計者也只能是讓指標在java語言中變得更加簡單、易用,而不能完全的將其剔除,所以才有了我們日常所見的關鍵字

null
登入後複製
登入後複製



空指標異常是一個運行時異常,對於這一類異常,如果沒有明確的處理策略,那麼最佳實踐在於讓程式早點掛掉,但是很多場景下,不是開發人員沒有具體的處理策略,而是根本沒有意識到空指標異常的存在。當異常真的發生的時候,處理策略也很簡單,在存在異常的地方添加一個if語句判定即可,但是這樣的應對策略會讓我們的程序出現越來越多的null判定,我們知道一個很好的程式設計,應該讓程式碼中盡量少出現null關鍵字,而java8所提供的

Optional
登入後複製

類別則在減少NullPointException的同時,也提升了程式碼的美觀。但首先我們需要明確的是,它並 不是對

null
登入後複製
登入後複製
關鍵字的一種替代,而是對於null判定提供了一種更優雅的實現,從而避免NullPointException

一. 直覺感受

假設我們需要回傳一個字串的長度,如果不借助第三方工具類,我們需要呼叫

str.length()
登入後複製

方法:

if(null == str) { // 空指標判定
   return 0;
}
return str.length();
如果採用Optional類,實作如下:

return Optional.ofNullable(str).map(String::length).orElse(0);
Optional的程式碼相對更加簡潔,當程式碼量較大時,我們很容易忘記進行null判定,但是使用Optional類別則會避免這類問題。

二.基本上使用

1.物件建立

#建立空白物件
##Optional上面的範例程式碼呼叫

empty()
登入後複製

方法建立了一個空的

Optional<String>
登入後複製

物件類型。



建立物件:不允許為空
Optional提供了方法

of()
登入後複製

用於建立非空對象,該方法要求傳入的參數不能為空,否則拋

NullPointException
登入後複製

,範例如下:


Optional<String> optStr = Optional.of(str);  // 當str為null的時候,將拋出NullPointException

建立物件:允許為空
如果無法確定傳入的參數是否存在null值的可能性,則可以用Optional的

ofNullable()
登入後複製

方法建立對象,如果入參為null,則建立一個空物件。範例如下:


Optional<String> optStr = Optional.ofNullable(str);  // 如果str是null,則建立一個空物件

2.串流處理

串流處理也是java8帶給我們的一個重量級新特性,讓我們對集合的操作變得更加簡潔和高效,下一篇關於java8新特性的文章,將對流失處理進行全面的講解。這裡Optional也提供了兩個基本的流失處理:映射和過濾。


為了演示,我們設計了一個

User
登入後複製

類,如下:



#

/**
 * @author: zhenchao.Wang 2016-9-24 15:36:56
 */
public class User {
    /** 用户编号 */
    private long id;
    private String name;
    private int age;
    private Optional phone;
    private Optional<String> email;
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 省略setter和getter
}
登入後複製

手機和郵箱不是一個人的必須有的,所以我們利用Optional定義。



映射:map與flatMap
映射是將輸入轉換成另外一種形式的輸出的操作,例如前面例子中,我們輸入字串,而輸出的是字串的長度,這就是一種隱射,我們利用方法

map()
登入後複製

得以實現。假設我們希望取得一個人的姓名,那麼我們可以如下實作:


String name = Optional.ofNullable(user).map(User::getName).orElse("no name");
這樣當入參user不為空的時候則返回其name,否則返回

no name
登入後複製

 如我我們希望通過上面方式得到phone或email,利用上面的方式則行不通了,因為map之後返回的是Optional,我們把這種稱為Optional嵌套,我們必須在map一次才能拿到我們想要的結果:


long phone = optUser.map(User::getPhone).map(Optional: :get).orElse(-1L);
其實這個時候,更好的方式是利用flatMap,一步拿到我們想要的結果:

long phone = optUser.flatMap(User:: getPhone).orElse(-1L);
flapMap可以將方法傳回的各個流扁平化成為一個流,具體在下一篇專門講流式處理的文章中細說。


過濾:fliter
filiter,顧名思義是過濾的操作,我們可以將過濾操作做為參數傳遞給該方法,從而實現過濾目的,加入我們希望篩選18歲以上的成年人,則可以實現如下:


optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));
登入後複製

3.預設行為


預設行為是當Optional為不滿足條件時所執行的操作,例如在上面的範例中我們使用的

orElse()
登入後複製

就是一个默认操作,用于在Optional对象为空时执行特定操作,当然也有一些默认操作是当满足条件的对象存在时执行的操作。

get()

get用于获取变量的值,但是当变量不存在时则会抛出

NoSuchElementException
登入後複製

,所以如果不确定变量是否存在,则不建议使用

orElse(T other)

当Optional的变量不满足给定条件时,则执行orElse,比如前面当str为null时,返回0。

orElseGet(Supplier<? extends X> expectionSupplier)
登入後複製

如果条件不成立时,需要执行相对复杂的逻辑,而不是简单的返回操作,则可以使用orElseGet实现:

long phone = optUser.map(User::getPhone).map(Optional::get).orElseGet(() -> {
    // do something here
    return -1L;
});
orElseThrow(Supplier<? extends X> expectionSupplier)
登入後複製

与get()方法类似,都是在不满足条件时返回异常,不过这里我们可以指定返回的异常类型。

ifPresent(Consumer)

当满足条件时执行传入的参数化操作。

三. 注意事项

Optional是一个final类,未实现任何接口,所以当我们在利用该类包装定义类的属性的时候,如果我们定义的类有序列化的需求,那么因为Optional没有实现Serializable接口,这个时候执行序列化操作就会有问题:

public class User implements Serializable{
    /** 用户编号 */
    private long id;
    private String name;
    private int age;
    private Optional phone;  // 不能序列化
    private Optional<String> email;  // 不能序列化
登入後複製

不过我们可以采用如下替换策略:

private long phone;
public Optional<Long> getPhone() {
    return Optional.ofNullable(this.phone);
}
登入後複製

看来Optional在设计的时候就没有考虑将它作为类的字段使用~

 以上就是Java8 新特性之 Optional 类 的内容,更多相关内容请关注PHP中文网(www.php.cn)!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板