摘要:當(dāng)滿足條件時執(zhí)行傳入的參數(shù)化操作。最后提醒一點,好用但不能濫用,在設(shè)計一個接口方法時是否采取類型返回需要斟酌,一味的使用會讓代碼變得比較啰嗦,反而破壞了代碼的簡潔性。鑒于作者水平有限,文中不免有錯誤之處,歡迎批評指正個人博客
NullPointException 可以說是所有 java 程序員都遇到過的一個異常,雖然 java 從設(shè)計之初就力圖讓程序員脫離指針的苦海,但是指針確實是實際存在的,而 java 設(shè)計者也只能是讓指針在 java 語言中變得更加簡單、易用,而不能完全的將其剔除,所以才有了我們?nèi)粘K姷降年P(guān)鍵字 null。
空指針異常是一個運行時異常,對于這一類異常,如果沒有明確的處理策略,那么最佳實踐在于讓程序早點掛掉,但是很多場景下不是開發(fā)人員沒有具體的處理策略,而是根本沒有意識到空指針異常的存在。當(dāng)異常真的發(fā)生的時候,處理策略也很簡單,在存在異常的地方添加一個 if 語句判定即可,但是這樣的應(yīng)對策略會讓我們的程序出現(xiàn)越來越多的 null 判定。一個良好的程序設(shè)計應(yīng)該讓代碼中盡量少出現(xiàn) null 關(guān)鍵字,而 8th 所提供的 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 的代碼相對更加簡潔,當(dāng)代碼量較大時,我們很容易忘記進行 null 判定,但是使用 Optional 類則會避免這類問題。
一. 基本使用 1.1 Optional 對象的創(chuàng)建創(chuàng)建空對象
OptionaloptStr = Optional.empty();
上面的示例代碼調(diào)用 empty() 方法創(chuàng)建了一個空的 Optional
創(chuàng)建對象:不允許我空
Optional 提供了方法 of() 用于創(chuàng)建非空對象,該方法要求傳入的參數(shù)不能為空,否則拋 NullPointException,示例如下:
OptionaloptStr = Optional.of(str); // 當(dāng)str為null的時候,將拋出NullPointException
創(chuàng)建對象:允許為空
如果不能確定傳入的參數(shù)是否存在 null 值的可能性,則可以用 Optional 的 ofNullable() 方法創(chuàng)建對象,如果入?yún)?null 則創(chuàng)建一個空對象。示例如下:
Optional1.2 流式數(shù)據(jù)處理optStr = Optional.ofNullable(str); // 如果str是null,則創(chuàng)建一個空對象
流式數(shù)據(jù)處理也是 8th 給我們帶來的一個重量級新特性,讓我們對集合的操作變得更加簡潔和高效,本系列下一篇將對流式數(shù)據(jù)處理進行全面的講解。Optional 類也提供了兩個基本的流失處理:映射和過濾。
為了演示,我們設(shè)計了一個 User 類,如下:
public class User { private long id; private String name; private int age; private Optionalphone; private Optional 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");
這樣當(dāng)入?yún)?user 不為空的時候則返回其 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 可以將方法返回的各個流扁平化成為一個流,具體在下一篇專門講流式數(shù)據(jù)處理的文章中細說。
過濾:fliter
filiter,顧名思義是過濾的操作,我們可以將過濾操作做為參數(shù)傳遞給該方法以實現(xiàn)過濾目的,假如我們希望篩選 18 周歲以上的成年人,則可以實現(xiàn)如下:
optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));1.3 默認行為
默認行為是當(dāng) Optional 在不滿足條件時所執(zhí)行的操作,比如在上面的例子中我們使用的 orElse() 就是一個默認操作,用于在 Optional 對象為空時執(zhí)行特定操作,當(dāng)然也有一些默認操作是當(dāng)滿足條件的對象存在時執(zhí)行的操作。
get()
get 方法用于獲取變量的值,但是當(dāng)變量不存在時則會拋出 NoSuchElementException,所以如果不確定變量是否存在則不建議使用
orElse(T other)
當(dāng) Optional 的變量不滿足給定條件時,則執(zhí)行 orElse,比如前面當(dāng) 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>)
當(dāng)滿足條件時執(zhí)行傳入的參數(shù)化操作。
二. 注意事項Optional 是一個 final 類且未實現(xiàn)任何接口,所以當(dāng)我們在利用該類包裝定義類的屬性的時候,如果我們定義的類有序列化的需求,那么因為 Optional 沒有實現(xiàn) Serializable 接口,這個時候執(zhí)行序列化操作就會有問題:
public class User implements Serializable { private long id; private String name; private int age; private Optionalphone; // 不能序列化 private Optional email; // 不能序列化 }
不過我們可以采用如下替換策略 Optinal:
private long phone; public OptionalgetPhone() { return Optional.ofNullable(this.phone); }
看來 Optional 類在設(shè)計的時候就沒有考慮將它作為類的字段使用。
最后提醒一點,Optional 好用但不能濫用,在設(shè)計一個接口方法時是否采取 Optional 類型返回需要斟酌,一味的使用會讓代碼變得比較啰嗦,反而破壞了代碼的簡潔性。
鑒于作者水平有限,文中不免有錯誤之處,歡迎批評指正
個人博客:www.zhenchao.org
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/70647.html
摘要:自定義函數(shù)式接口我們在前面例子中實現(xiàn)的蘋果篩選接口就是一個函數(shù)式接口定義如下,正因為如此我們可以將篩選邏輯參數(shù)化,并應(yīng)用表達式僅包含一個抽象方法,依照定義可以將其視為一個函數(shù)式接口。 Lambda 表達式是 java 8th 給我們帶來的幾個重量級新特性之一,借用 lambda 表達式可以讓我們的程序設(shè)計更加簡潔。最近新的項目摒棄了 6th 版本,全面基于 8th 進行開發(fā),本文將探討...
摘要:新特性總覽標(biāo)簽本文主要介紹的新特性,包括表達式方法引用流默認方法組合式異步編程新的時間,等等各個方面。還有對應(yīng)的和類型的函數(shù)連接字符串廣義的歸約匯總起始值,映射方法,二元結(jié)合二元結(jié)合。使用并行流時要注意避免共享可變狀態(tài)。 Java8新特性總覽 標(biāo)簽: java [TOC] 本文主要介紹 Java 8 的新特性,包括 Lambda 表達式、方法引用、流(Stream API)、默認方...
摘要:但有一個限制它們不能修改定義的方法的局部變量的內(nèi)容。如前所述,這種限制存在的原因在于局部變量保存在棧上,并且隱式表示它們僅限于其所在線程。 2014年,Oracle發(fā)布了Java8新版本。對于Java來說,這顯然是一個具有里程碑意義的版本。尤其是那函數(shù)式編程的功能,避開了Java那煩瑣的語法所帶來的麻煩。 這可以算是一篇Java8的學(xué)習(xí)筆記。將Java8一些常見的一些特性作了一個概要的...
大概一年多之前,我對java8的理解還僅限一些只言片語的文章之上,后來出于對函數(shù)式編程的興趣,買了本參考書看了一遍,然后放在了書架上,后來,當(dāng)我接手大客戶應(yīng)用的開發(fā)工作之后,java8的一些工具,對我的效率有了不小的提升,因此想記錄一下java8的一些常用場景,我希望這會成為一個小字典,能讓我免于頻繁翻書,但是總能找到自己想找的知識。 用于舉例的model: @Data public class ...
摘要:函數(shù)副作用會給程序設(shè)計帶來不必要的麻煩,引入潛在的,并降低程序的可讀性。所以只能采用這種曲線救國的方式。則是把這種曲線救國拿到了臺面上,并昭告天下,同時還對提供了一些語法支持。是自由變量,提供執(zhí)行上下文,觸發(fā)閉包執(zhí)行。 背景 自從2013年放棄了Java就再也沒有碰過。期間Java還發(fā)布了重大更新:引入lambda,但是那會兒我已經(jīng)玩了一段時間Scala,對Java已經(jīng)瞧不上眼。相比S...
閱讀 3094·2021-09-22 15:54
閱讀 3981·2021-09-09 11:34
閱讀 1767·2019-08-30 12:48
閱讀 1161·2019-08-30 11:18
閱讀 3431·2019-08-26 11:48
閱讀 913·2019-08-23 17:50
閱讀 2119·2019-08-23 17:17
閱讀 1240·2019-08-23 17:12