在開發(fā)過(guò)程中,空指針異常是最常見,不過(guò)也是比較容易修改的。盡管如此,為了避免空指針,我們可能會(huì)加入大量的檢測(cè)邏輯。好在Java8中為我們提供了Optional類,它擁有一整套完善的為空檢測(cè)及處理邏輯,大大的方便了我們的開發(fā)。
Optional類實(shí)際上就是一個(gè)容器,里面保存著我們的對(duì)象,并提供取方法,并且可以為存為null的對(duì)象。
創(chuàng)建一個(gè)Optional對(duì)象:
1.Optional.of(obj)
這個(gè)方法需要傳入一個(gè)非空的對(duì)象
2.Optional.ofNullable(obj)
這個(gè)不同于上面的方法,它允許傳入一個(gè)為空的對(duì)象
3.Optional.empty()
嚴(yán)格意義來(lái)說(shuō),這個(gè)并不算,他是返回一個(gè)空的Optional實(shí)例,注意,并不是為null的實(shí)例,而是不包含其他對(duì)象的實(shí)例。源碼如下:
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
具體使用
獲得了一個(gè)Optional實(shí)例后,關(guān)鍵就是怎么好好使用。首先這個(gè)類提供了一個(gè)為空判斷方法:isPresent(),但是千萬(wàn)不要單純的使用這個(gè)方法,比如:
private void test(Person person){
Optional<Person> optional = Optional.ofNullable(person);
if(optional.isPresent()){
//...
return;
}
//...
}
那簡(jiǎn)直是對(duì)這個(gè)類的一大浪費(fèi),而且又無(wú)故多寫了許多代碼。我們可以看一下isPresent()的實(shí)現(xiàn):
public boolean isPresent() {
return value != null;
}
這不就是我們經(jīng)常寫的為空判斷么。而且大致看一眼這個(gè)類的方法,也能感覺(jué)到它不僅僅只是做一個(gè)判斷這么簡(jiǎn)單。接下來(lái),我們就一起來(lái)學(xué)習(xí)一下其他那些非常有用的方法,之后你也許就會(huì)感受到設(shè)計(jì)人員的良苦用心了。
1.get()
首先我們來(lái)看看取對(duì)象的方法,在以此為擴(kuò)展了解其他方法。Optional既然是一個(gè)容器,那么肯定能存能取,不然要他干嘛?
這個(gè)方法語(yǔ)義很清晰,就是取之前我們保存的對(duì)象,當(dāng)對(duì)象為空時(shí),拋出空指針異常。可能有人要問(wèn)了,既然只是簡(jiǎn)單的取值,而且還是會(huì)拋異常,哪有何用?對(duì),這個(gè)方法本身并沒(méi)啥用,就是一個(gè)簡(jiǎn)單的get方法,除非你配合isPresent(),但那樣又回到了以前的思維,所以我們主要來(lái)看后面的方法。
2.orElse(T other)
這個(gè)就比較使用了,他要求傳入一個(gè)默認(rèn)值,當(dāng)我們之前保存的值為空時(shí),就返回傳入的默認(rèn)值,否則返回之前保存的值。
public static void main(String[] args){
test(null); //10
test(new Person(15)); //15
}
public static void test(Person person){
Optional<Person> optional = Optional.ofNullable(person);
out.println(optional.orElse(new Person(10)).getAge());
}
幫我們省略了一些if-else邏輯,是不是很人性化?不要急著滿足,后面還有更精彩的。
3.orElseGet(Supplier<? extends T> other)
這個(gè)與上面類似,只不過(guò)為空時(shí)不再返回一個(gè)默認(rèn)值,而是通過(guò)提供的方法,返回這個(gè)方法所返回的值:
public static void main(String[] args){
test(null); //10
test(new Person(15)); //15
}
public static void test(Person person){
Optional<Person> optional = Optional.ofNullable(person);
out.println(optional.orElseGet(()->new Person(10)).getAge());
}
(為了簡(jiǎn)潔,我一般都用lambda表達(dá)式形式書寫代碼,如果對(duì)lambda表達(dá)式不熟悉的,可以參考我的這篇文章)
4.ifPresent(Consumer<? super T> consumer)
一般來(lái)說(shuō),我們進(jìn)行為空判斷后肯定要接著執(zhí)行一些操作。所以,設(shè)計(jì)人員為我們提供了這個(gè)方法,基本功能就是如果我們所存的對(duì)象不為空時(shí),執(zhí)行所指定的代碼,為空時(shí)什么都不做:
public static void test(Person person){
Optional<Person> optional = Optional.ofNullable(person);
optional.ifPresent(p -> out.println(p.getAge()));
}
這樣一來(lái)為空判斷和后續(xù)邏輯就連貫在一起了。
5.map(Function<? super T,? extends U> mapper)
有時(shí)候,我們從某個(gè)不為空的對(duì)象上使用某些方法后,也會(huì)生成空對(duì)象,因此就要要連續(xù)多級(jí)連續(xù)判斷,放在以前,就要大量if-else嵌套,而map函數(shù)很方便這些操作:
optional.map(p -> p.getName())
.map(n -> n.toUpperCase())
.orElse("ABC")
換成等價(jià)的if-else代碼
if (person != null) {
String name = person.getName();
if (name != null) {
out.println(name.toUpperCase());
}else{
out.println("ABC");
}
}else{
out.println("ABC");
}
map和ifPresent類似,也是在不為空時(shí)執(zhí)行指定邏輯,只不過(guò)map返回的也是一個(gè)Optional,所以map鏈可以無(wú)限延長(zhǎng),代替了if嵌套if的語(yǔ)句,不僅簡(jiǎn)單而且結(jié)構(gòu)清晰。如果某一環(huán)為空,則不會(huì)執(zhí)行所指定的邏輯,返回一個(gè)空的Optional實(shí)例。所以我們可以在某一環(huán)添加一些其他的方法,進(jìn)行為空時(shí)的邏輯處理。類似的自由組合可以創(chuàng)造無(wú)限可能。
6.filter(Predicate<? super T> predicate)
了解過(guò)流式編程的朋友看到Optional的這些方法可能會(huì)很熟悉(如果不熟悉也沒(méi)關(guān)系,在下一篇文章中學(xué)習(xí)過(guò)Stream類后,你就會(huì)很了解這類編程風(fēng)格),這些方法應(yīng)該是這一類API的標(biāo)準(zhǔn)方法,所以怎能少的了filter?
顧名思義,這個(gè)方法就是過(guò)濾的意思,只有符合我們所指定的邏輯后,才會(huì)向下傳遞,否者傳遞一個(gè)空的Optional,當(dāng)然,身為空指針檢測(cè)類,他的前提條件當(dāng)然是不為空,否則不會(huì)執(zhí)行傳入的代碼:
optional.filter(p -> p.getAge()>18).orElse(new Person(19, "jack")).getAge()
這個(gè)就很方便我們?cè)诓粸榭盏耐瑫r(shí)進(jìn)行邏輯判斷,同時(shí)由于filter也是返回一個(gè)Optional對(duì)象,所以也可以搭配其他方法使用。
7.flatMap(Function<? super T,Optional<U>> mapper)
這個(gè)方法名字中也有一個(gè)map,那么應(yīng)該可map有關(guān)聯(lián)。對(duì)的,他的功能和map差不多,都是類似轉(zhuǎn)換功能,但是,map的返回值是自動(dòng)幫我們包裝好的Optional對(duì)象,而flatmap這需要我們自己包裝一個(gè)Optional對(duì)象:
optional.flatMap(p -> Optional.ofNullable(p.getName()))
可見flatMap靈活性較map大一點(diǎn),不過(guò)map既然這樣設(shè)計(jì)也是方便我們開發(fā),不一定每次為空檢測(cè)后,都要返回一個(gè)不相干的類型,所以如何取舍,還是自己決定。ψ(*`ー′)ψ
8.orElseThrow(Supplier<? extends X> exceptionSupplier)
Optional類的方法并不多,到這里我們已經(jīng)講得差不多了,但是是不是覺(jué)得少點(diǎn)什么呢?原來(lái)前面的方法基本上都是檢測(cè)為空時(shí),直接不執(zhí)行了,這樣雖然不會(huì)報(bào)錯(cuò),但是對(duì)于我們后期維護(hù)檢測(cè)問(wèn)題并不好。
Optional.of雖然會(huì)拋異常,但是他是在一開始包裝的時(shí)候就拋,如果這個(gè)對(duì)象不為空,對(duì)象的成員為空呢?總不能還要用get()方法吧,get雖然可以,但只是拋一個(gè)傳統(tǒng)的空指針異常,如果要拋?zhàn)远x異常呢?
這時(shí)候就要用orElseThrow了,他在為空時(shí)會(huì)拋一個(gè)我們指定的異常,由于傳遞的是代碼塊,所以我們也可以在拋異常前,執(zhí)行一些其他邏輯:
optional.orElseThrow(()->{out.println("hello");return new Throwable("abc");});
小結(jié)
這個(gè)類主要功能就是空指針檢測(cè),只不過(guò)設(shè)計(jì)人員為我們添加了許多實(shí)用的擴(kuò)展方法,節(jié)省了我們大量代碼,總體來(lái)說(shuō)是很不錯(cuò)的。有興趣的朋友可以看一看這個(gè)類是怎么實(shí)現(xiàn)的,由于是相對(duì)獨(dú)立的,所以理解起來(lái)難度并不高。而且這個(gè)類的方法都是比較簡(jiǎn)單地,基礎(chǔ)好的朋友也許自己都能創(chuàng)造出類似的類。
總之,既然為我們提供了這么實(shí)用的工具,我們就要在以后的實(shí)踐中盡量有意識(shí)的多用用,不要再像以前一樣if...else... ?乛乛?