Optional不是對null關(guān)鍵字的一種替代,而是對于null判定提供了一種更加優(yōu)雅的實現(xiàn)。
NullPointException可以說是所有java程序員都遇到過的一個異常,雖然java從設(shè)計之初就力圖讓程序員脫離指針的苦海,但是指針確實是實際存在的,而java設(shè)計者也只能是讓指針在java語言中變得更加簡單、易用,而不能完全的將其剔除,所以才有了我們?nèi)粘K姷降年P(guān)鍵字null。
空指針異常是一個運行時異常,對于這一類異常,如果沒有明確的處理策略,那么最佳實踐在于讓程序早點掛掉,但是很多場景下,不是開發(fā)人員沒有具體的處理策略,而是根本沒有意識到空指針異常的存在。當異常真的發(fā)生的時候,處理策略也很簡單,在存在異常的地方添加一個if語句判定即可,但是這樣的應(yīng)對策略會讓我們的程序出現(xiàn)越來越多的null判定,我們知道一個良好的程序設(shè)計,應(yīng)該讓代碼中盡量少出現(xiàn)null關(guān)鍵字,而java8所提供的Optional類則在減少NullPointException的同時,也提升了代碼的美觀度。但首先我們需要明確的是,它并 不是對null關(guān)鍵字的一種替代,而是對于null判定提供了一種更加優(yōu)雅的實現(xiàn),從而避免NullPointException。
一、直觀感受
假設(shè)我們需要返回一個字符串的長度,如果不借助第三方工具類,我們需要調(diào)用str.length()方法:
if(null == str) { // 空指針判定
return 0;
}
return str.length();
如果采用Optional類,實現(xiàn)如下:
return Optional.ofNullable(str).map(String::length).orElse(0);
Optional的代碼相對更加簡潔,當代碼量較大時,我們很容易忘記進行null判定,但是使用Optional類則會避免這類問題。
二、 基本使用
1.對象創(chuàng)建
創(chuàng)建空對象
Optional《String》 optStr = Optional.empty();
上面的示例代碼調(diào)用empty()方法創(chuàng)建了一個空的Optional《String》對象型。
創(chuàng)建對象:不允許為空
Optional提供了方法of()用于創(chuàng)建非空對象,該方法要求傳入的參數(shù)不能為空,否則拋NullPointException,示例如下:
Optional《String》 optStr = Optional.of(str); // 當str為null的時候,將拋出NullPointException
創(chuàng)建對象:允許為空
如果不能確定傳入的參數(shù)是否存在null值的可能性,則可以用Optional的ofNullable()方法創(chuàng)建對象,如果入?yún)閚ull,則創(chuàng)建一個空對象。示例如下:
Optional《String》 optStr = Optional.ofNullable(str); // 如果str是null,則創(chuàng)建一個空對象
2.流式處理
流式處理也是java8給我們帶來的一個重量級新特性,讓我們對集合的操作變得更加簡潔和高效,后面的文章將對流失處理進行全面的講解。這里Optional也提供了兩個基本的流失處理:映射和過濾。
對于如下一個User類:
public class User {
/** 用戶編號 */
private long id;
private String name;
private int age;
private Optional《Long》 phone;
private Optional《String》 email;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// 省略setter和getter
}
手機和郵箱不是一個人的必須有的,所以我們利用Optional定義。
映射:map與flatMap
映射是將輸入轉(zhuǎn)換成另外一種形式的輸出的操作,比如前面例子中,我們輸入字符串,而輸出的是字符串的長度,這就是一種隱射,我們利用方法map()得以實現(xiàn)。假設(shè)我們希望獲得一個人的姓名,那么我們可以如下實現(xiàn):
String name = Optional.ofNullable(user).map(User::getName).orElse(“no name”);
這樣當入?yún)ser不為空的時候則返回其name,否則返回no name 如我我們希望通過上面方式得到phone或email,利用上面的方式則行不通了,因為map之后返回的是Optional,我們把這種稱為Optional嵌套,我們必須在map一次才能拿到我們想要的結(jié)果:
long phone = optUser.map(User::getPhone).map(Optional::get).orElse(-1L);
其實這個時候,更好的方式是利用flatMap,一步拿到我們想要的結(jié)果:
long phone = optUser.flatMap(User::getPhone).orElse(-1L);
flapMap可以將方法返回的各個流扁平化成為一個流,后面的文章中細說。
過濾:fliter
filiter,顧名思義是過濾的操作,我們可以將過濾操作做為參數(shù)傳遞給該方法,從而實現(xiàn)過濾目的,加入我們希望篩選18周歲以上的成年人,則可以實現(xiàn)如下:
optUser.filter(u -》 u.getAge() 》= 18).ifPresent(u -》 System.out.println(“Adult:” + u));
3.默認行為
默認行為是當Optional為不滿足條件時所執(zhí)行的操作,比如在上面的例子中我們使用的orElse()就是一個默認操作,用于在Optional對象為空時執(zhí)行特定操作,當然也有一些默認操作是當滿足條件的對象存在時執(zhí)行的操作。
get()
get用于獲取變量的值,但是當變量不存在時則會拋出NoSuchElementException,所以如果不確定變量是否存在,則不建議使用
orElse(T other)
當Optional的變量不滿足給定條件時,則執(zhí)行orElse,比如前面當str為null時,返回0。
orElseGet(Supplier《? extends X》 expectionSupplier)
如果條件不成立時,需要執(zhí)行相對復(fù)雜的邏輯,而不是簡單的返回操作,則可以使用orElseGet實現(xiàn):
long phone = optUser.map(User::getPhone).map(Optional::get).orElseGet(() -》 {
// do something here
return -1L;
});
orElseThrow(Supplier《? extends X》 expectionSupplier)
與get()方法類似,都是在不滿足條件時返回異常,不過這里我們可以指定返回的異常類型。
ifPresent(Consumer《? super T》)
當滿足條件時執(zhí)行傳入的參數(shù)化操作。
三、 注意事項
Optional是一個final類,未實現(xiàn)任何接口,所以當我們在利用該類包裝定義類的屬性的時候,如果我們定義的類有序列化的需求,那么因為Optional沒有實現(xiàn)Serializable接口,這個時候執(zhí)行序列化操作就會有問題:
public class User implements Serializable{
/** 用戶編號 */
private long id;
private String name;
private int age;
private Optional《Long》 phone; // 不能序列化
private Optional《String》 email; // 不能序列化
不過我們可以采用如下替換策略:
private long phone;
public Optional《Long》 getPhone() {
return Optional.ofNullable(this.phone);
}
Optional在設(shè)計的時候就沒有考慮將它作為類的字段使用~
評論
查看更多