淺談Java8的特性之Optional類
一、Optional類 簡(jiǎn)介
Optional類是 Java 8 引入的一個(gè)很有趣的特性。它主要解決的問題是臭名昭著的空指針異常(NullPointerException)
- Optional 類是一個(gè)可以為null的容器對(duì)象。如果值存在則isPresent()方法會(huì)返回true,調(diào)用get()方法會(huì)返回該對(duì)象。
- Optional 是個(gè)容器:它可以保存類型T的值,或者僅僅保存null。Optional提供很多有用的方法,這樣我們就不用顯式進(jìn)行空值檢測(cè)。
- Optional 類的引入很好的解決空指針異常。
Optional 是一個(gè)對(duì)象容器,具有以下兩個(gè)特點(diǎn):
- 提示用戶要注意該對(duì)象有可能為null
- 簡(jiǎn)化if else代碼
舉一個(gè)簡(jiǎn)單的例子,在 Java 8 之前,任何訪問對(duì)象方法或?qū)傩缘恼{(diào)用都可能導(dǎo)致 NullPointerException:
用戶 -> 家庭住址 -> 城市 ->郵編 String postCode = user.getAddress().getCity().getPostCode();
在這個(gè)示例中,為了避免異常,就得在訪問每一個(gè)值之前對(duì)其進(jìn)行明確地檢查:
if (user != null) { Address address = user.getAddress(); if (address != null) { City city= address.getCity(); if (city != null) { String postCode = city.getPostCode(); if (postCode != null) { //對(duì)postCode進(jìn)行操作 test(postCode); } } } }
這很容易就變得冗長(zhǎng),難以維護(hù)。 為了簡(jiǎn)化這個(gè)過程,我們就可以用 Optional 類。
二、Optional類的使用
1. 創(chuàng)建:
Optional類的實(shí)例創(chuàng)建有三種方式:
- Optional.empty() :創(chuàng)建一個(gè)空的 Optional 實(shí)例。
- Optional.of(T t) :創(chuàng)建一個(gè) Optional 實(shí)例,當(dāng) t為null時(shí)拋出異常(NullPointerException)。
- Optional.ofNullable(T t) :創(chuàng)建一個(gè) Optional 實(shí)例,但當(dāng) t為null時(shí)不會(huì)拋出異常,而是返回一個(gè)空的實(shí)例。
2. 獲?。?/h3>
- get():獲取optional實(shí)例中的對(duì)象,當(dāng)optional 容器為空時(shí)報(bào)錯(cuò)。
3. 判斷:
- isPresent():判斷optional是否為空,如果空則返回false,否則返回true
- ifPresent(Consumer c):如果optional不為空,則將optional中的對(duì)象傳給Comsumer函數(shù)
public class OptionalDemo { public static void main(String[] args) { User user = new User("王也", "5"); User userNull= null; Optional<User> optional = Optional.ofNullable(user); System.out.println(optional.isPresent()); optional.ifPresent(u -> System.out.println("optional不為null "+u)); } }
- orElse(T other):如果optional不為空,則返回optional中的對(duì)象;如果為null,則返回 other 這個(gè)默認(rèn)值
User user = new User("王也", "5"); User userNull= null; //orElse的工作方式非常直接,如果有值則返回該值,否則返回傳遞給它的參數(shù)值: User user1 = Optional.ofNullable(userNull).orElse(user); System.out.println(user1); //控制臺(tái)輸出:User(name=王也, age=5)
- orElseGet(Supplier other):如果optional不為空,則返回optional中的對(duì)象;如果為null,則使用Supplier函數(shù)生成默認(rèn)值other
User user1 = Optional.ofNullable(userNull).orElseGet(()->user ); //結(jié)果同上
- orElseThrow(Supplier exception):如果optional不為空,則返回optional中的對(duì)象;如果為null,則拋出Supplier函數(shù)生成的異常
//這個(gè)方法讓我們有更豐富的語義,可以決定拋出什么樣的異常,而不總是拋出 NullPointerException。 User result = Optional.ofNullable(userNull) .orElseThrow( () -> new IllegalArgumentException());
orElse() 和 orElseGet() 的不同之處
乍一看,這兩種方法似乎起著同樣的作用。然而事實(shí)并非如此。我們創(chuàng)建一些示例來突出二者行為上的異同。
(1)當(dāng)對(duì)象為空時(shí):
public class OptionalDemo { public static void main(String[] args) { User userNull = null; System.out.println("使用orElse():"); User result = Optional.ofNullable(userNull).orElse(createNewUser()); System.out.println("使用orElseGet():"); User result2 = Optional.ofNullable(userNull).orElseGet(() -> createNewUser()); } private static User createNewUser() { System.out.println("Creating New User"); return new User("新的user對(duì)象", "1234"); } }
上面的代碼中,兩種方法都調(diào)用了 createNewUser() 方法,這個(gè)方法會(huì)記錄一個(gè)消息并返回 User 對(duì)象。
控制臺(tái)輸出:
使用orElse():
Creating New User
使用orElseGet():
Creating New User
由此可見,當(dāng)對(duì)象為空而返回默認(rèn)對(duì)象時(shí),行為并無差異。
(2)當(dāng)對(duì)象不為空時(shí):
public class OptionalDemo { public static void main(String[] args) { User user = new User("王也", "5"); System.out.println("使用orElse():"); User result = Optional.ofNullable(user).orElse(createNewUser()); System.out.println("使用orElseGet():"); User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); } private static User createNewUser() { System.out.println("Creating New User"); return new User("新的user對(duì)象", "1234"); } }
控制臺(tái)輸出:
使用orElse():
Creating New User
使用orElseGet():
這個(gè)示例中,兩個(gè) Optional 對(duì)象都包含非空值,兩個(gè)方法都會(huì)返回對(duì)應(yīng)的非空值。不過,orElse() 方法仍然創(chuàng)建了 User 對(duì)象。與之相反,orElseGet() 方法不創(chuàng)建 User 對(duì)象。
在執(zhí)行較密集的調(diào)用時(shí),比如調(diào)用 Web 服務(wù)或數(shù)據(jù)查詢,這個(gè)差異會(huì)對(duì)性能產(chǎn)生重大影響。
4. 過濾:
filter(Predicate p):filter() 接受一個(gè) Predicate 參數(shù),返回測(cè)試結(jié)果為 true 的值。如果測(cè)試結(jié)果為 false,會(huì)返回一個(gè)空的 Optional。
5. 映射:
- map(Function<T, U> mapper):如果optional不為空,則將optional中的對(duì)象 t 映射成另外一個(gè)對(duì)象 u,并將 u 存放到一個(gè)新的optional容器中。
- flatMap(Function<T,Optional > mapper):跟上面一樣,在optional不為空的情況下,將對(duì)象t映射成另外一個(gè)optional
map() 對(duì)值應(yīng)用(調(diào)用)作為參數(shù)的函數(shù),然后將返回的值包裝在 Optional 中。這就使對(duì)返回值進(jìn)行鏈試調(diào)用的操作成為可能 —— 這里的下一環(huán)就是 orElse()。
flatMap() 也需要函數(shù)作為參數(shù),并對(duì)值調(diào)用這個(gè)函數(shù),然后直接返回結(jié)果。
過濾和映射的示例
public class OptionalDemo { public static void main(String[] args) { List<Student> studentList = initData(); for (Student student : studentList) { Optional<Student> studentOptional = Optional.of(student); Integer score = studentOptional.filter(s -> s.getAge() >= 18).map(Student::getScore).orElse(0); if (score > 80) { System.out.println("入選:" + student.getName()); } } } private static List<Student> initData(){ Student s1 = new Student("張三", 19, 80); Student s2 = new Student("李四", 19, 50); Student s3 = new Student("王五", 23, null); Student s4 = new Student("趙六", 16, 90); Student s5 = new Student("錢七", 18, 99); Student s6 = new Student("孫八", 20, 40); Student s7 = new Student("吳九", 21, 88); return Arrays.asList(s1, s2, s3, s4, s5, s6, s7); } }
使用stream流的寫法:
public static void main(String[] args) { List<Student> studentList = initData(); List<String> studentName = studentList.stream().filter(student -> student.getAge() >= 18) .filter(student -> student.getScore() != null && student.getScore() > 80) .map(student -> student.getName()).collect(Collectors.toList()); System.out.println(studentName); }
控制臺(tái)輸出:
入選:錢七
入選:吳九
到此這篇關(guān)于淺談Java8的特性之Optional類的文章就介紹到這了,更多相關(guān)Java的Optional類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java List分頁功能實(shí)現(xiàn)代碼實(shí)例
這篇文章主要介紹了Java List分頁功能實(shí)現(xiàn)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01Java如何實(shí)現(xiàn)驗(yàn)證碼驗(yàn)證功能
這篇文章主要教大家如何實(shí)現(xiàn)Java驗(yàn)證碼驗(yàn)證功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02Java對(duì)List進(jìn)行排序的方法總結(jié)
在Java中,對(duì)List進(jìn)行排序是一項(xiàng)常見的任務(wù),Java提供了多種方法來對(duì)List中的元素進(jìn)行排序,本文將詳細(xì)介紹如何使用Java來實(shí)現(xiàn)List的排序操作,涵蓋了常用的排序方法和技巧,需要的朋友可以參考下2024-07-07對(duì)java for 循環(huán)執(zhí)行順序的詳解
今天小編就為大家分享一篇對(duì)java for 循環(huán)執(zhí)行順序的詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-06-06Java的GUI編程之列表和組合框的設(shè)計(jì)使用
這篇文章主要介紹了Java的GUI編程之列表和組合框的設(shè)計(jì)使用,是Java圖形界面編程中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10一文詳解Elasticsearch和MySQL之間的數(shù)據(jù)同步問題
Elasticsearch中的數(shù)據(jù)是來自于Mysql數(shù)據(jù)庫(kù)的,因此當(dāng)數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行增刪改后,Elasticsearch中的數(shù)據(jù),索引也必須跟著做出改變。本文主要來和大家探討一下Elasticsearch和MySQL之間的數(shù)據(jù)同步問題,感興趣的可以了解一下2023-04-04java實(shí)現(xiàn)文件復(fù)制、剪切文件和刪除示例
這篇文章主要介紹了java實(shí)現(xiàn)文件復(fù)制、剪切文件和刪除示例,需要的朋友可以參考下2014-04-04