摘要: Optional不是對(duì)null關(guān)鍵字的一種替代斟冕,而是對(duì)于null判定提供了一種更加優(yōu)雅的實(shí)現(xiàn)
Java8新特性系列
- Java8新特性(一) – lambda表達(dá)式
- Java8新特性(二) – Optional類(lèi)
- Java8新特性(三) – 流式數(shù)據(jù)處理
- Java8新特性(四) – 默認(rè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ì)于這一類(lèi)異常桨醋,如果沒(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
類(lèi)則在減少NullPointException的同時(shí)秉扑,也提升了代碼的美觀度慧邮。但首先我們需要明確的是,它并 不是對(duì)null
關(guān)鍵字的一種替代舟陆,而是對(duì)于null判定提供了一種更加優(yōu)雅的實(shí)現(xiàn)误澳,從而避免NullPointException。
一. 直觀感受
假設(shè)我們需要返回一個(gè)字符串的長(zhǎng)度秦躯,如果不借助第三方工具類(lèi)忆谓,我們需要調(diào)用str.length()方法:
if(null == str) { // 空指針判定
return 0;
}
return str.length();
如果采用Optional類(lèi),實(shí)現(xiàn)如下:
return Optional.ofNullable(str).map(String::length).orElse(0);
Optional的代碼相對(duì)更加簡(jiǎn)潔踱承,當(dāng)代碼量較大時(shí)倡缠,我們很容易忘記進(jìn)行null判定,但是使用Optional類(lèi)則會(huì)避免這類(lèi)問(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)潔和高效肢娘,下一篇關(guān)于java8新特性的文章呈础,將對(duì)流失處理進(jìn)行全面的講解舆驶。這里Optional也提供了兩個(gè)基本的流失處理:映射和過(guò)濾。
為了演示猪落,我們?cè)O(shè)計(jì)了一個(gè)User類(lèi)贞远,如下:
/**
* @author: zhenchao.Wang 2016-9-24 15:36:56
*/
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促王,我們把這種稱(chēng)為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è)流阅畴,具體在下一篇專(zhuān)門(mén)講流式處理的文章中細(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()方法類(lèi)似尔艇,都是在不滿足條件時(shí)返回異常尔许,不過(guò)這里我們可以指定返回的異常類(lèi)型。
ifPresent(Consumer<? super T>)
當(dāng)滿足條件時(shí)執(zhí)行傳入的參數(shù)化操作终娃。
三. 注意事項(xiàng)
Optional是一個(gè)final類(lèi)味廊,未實(shí)現(xiàn)任何接口,所以當(dāng)我們?cè)诶迷擃?lèi)包裝定義類(lèi)的屬性的時(shí)候棠耕,如果我們定義的類(lèi)有序列化的需求余佛,那么因?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);
}
看來(lái)Optional在設(shè)計(jì)的時(shí)候就沒(méi)有考慮將它作為類(lèi)的字段使用~
萬(wàn)水千山總是情窍荧,點(diǎn)個(gè)****"關(guān)注"****行不行;匝病!蕊退!