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