Java新特性之Optional類超詳細介紹
前言
Optional 類是 Java 8 才引入的,Optional 是個容器,它可以保存類型 T 的值,或者僅僅保存 null。Optional 提供了很多方法,這樣我們就不用顯式進行空值檢測。Optional 類的引入很好的解決空指針異常。
Java 8 引入 Optional 類,用來解決 NullPointerException。 Optional 代替
if…else
解決空指針問題,使代碼更加簡潔。
1、Optional類概述
1.1、Optional類介紹
Optional 類是 Java 8 才引入的,Optional 是個容器,它可以保存類型 T 的值,或者僅僅保存 null。Optional 提供了很多方法,這樣我們就不用顯式進行空值檢測。Optional 類的引入很好的解決空指針異常。
Java 8 引入 Optional 類,用來解決 NullPointerException。 Optional 代替 if…else
解決空指針問題,使代碼更加簡潔。
1.2、Java8之前的空指針異常判斷
Java 在使用對象過程中,訪問任何方法或?qū)傩远伎赡軐?dǎo)致 NullPointerException:
例如我們通過以下方法,獲取存在 student 對象中的 Age 值。
public String getIsocode (Student student){ return student.getAge(); }
在這樣的示例中,如果我們想要避免由 student
或 student.age
為空而導(dǎo)致的空指針問題,我們就需要采用防御式檢查減少 NullPointerException(在訪問每一個值之前對其進行明確地檢查):
public String getIsocode (Student student){ if (null == student) { // doSomething return "Unknown"; } if (null = student.getAge()) { // doSomething return "Unknown"; } return student.getAge(); }
然而,這種方案并不是很理想,因為為此會多出多個不同的退出點(return),使得代碼維護變得艱難,之后每個可能的 null 檢查都會新增一個退出點。
為了簡化這個過程,我們來看看用 Optional 類是怎么做的。
1.3、Java8之后Optional的使用
當需要判斷的量多時,此時的這些判斷語句可能會導(dǎo)致代碼臃腫冗余,為此 Java8 特意推出了 Optional 類來幫助我們?nèi)ヌ幚砜罩羔槷惓!?/p>
下面是 Optional 的一些基本用法:
@Data public class Student { private Integer age; } --- public class Test { public static void main(String[] args) { // 假設(shè) student 這個對象從數(shù)據(jù)庫中查出的 Student student = getStudent(); // 創(chuàng)建一個可接受 null 的 Optiona l類 Optional<Student> optional = Optional.ofNullable(student); // 用法1:獲取 student 對象中的某個值,如果不存在的話則取默認值(不具有短路作用) Integer a1 = optional.map(Student::getAge).orElse(20)); // 用法2:獲取 student 對象中的某個值,如果不存在的話則取默認值(具有短路作用,因為是懶加載) Integer a2 = optional.map(Student::getAge).orElseGet(() -> Integer.MAX_VALUE); // 用法3:判斷對象是否存在,不存在則拋出異常 optional.orElseThrow(() -> new RuntimeException("student不存在!")); // 用法4:判斷對象是否存在,存在的話對對象進行操作,例如給對象賦初始值 optional.ifPresent(o -> o.setAge(18)); // 用法5:對象存在時,且年齡滿足一定條件容器才會繼續(xù)保存這對象,否則將會剔除 optional.filter(o -> o.getAge() > 10); } }
2、Optional類使用
2.1、Optional類常用方法總結(jié)
方法 | 描述 |
---|---|
empty | 返回一個空的 Optional 實例 |
filter | 如果值存在并且滿足提供的謂詞,就返回包含該值的 Optional 對象;否則返回一個空的 Optional 對象 |
flatMap | 如果值存在,就對該值執(zhí)行提供的 mapping 函數(shù)調(diào)用,返回一個 Optional 類型的值,否則就返回一個空的 Optional 對象 |
get | 如果該值存在,將該值用 Optional 封裝返回,否則拋出一個 NoSuchElementException 異常 |
ifPresent | 如果值存在,就執(zhí)行使用該值的方法調(diào)用,否則什么也不做 |
isPresent | 如果值存在就返回 true,否則返回 false |
map | 如果值存在,就對該值執(zhí)行提供的mapping 函數(shù)調(diào)用 |
of | 將指定值用 Optional 封裝之后返回,如果該值為 null,則拋出一個 NullPointerException 異常 |
ofNullable | 將指定值用 Optional 封裝之后返回,如果該值為 null,則返回一個空的 Optional 對象 |
orElse | 如果有值則將其返回,否則返回一個默認值 |
orElseGet | 如果有值則將其返回,否則返回一個由指定的 Supplier 接口生成的值 |
orElseThrow | 如果有值則將其返回,否則拋出一個由指定的 Supplier 接口生成的異常 |
2.2、Optional對象創(chuàng)建
2.2.1、Optional.empty()方法
使用 Optional.empty()
方法聲明一個空的 Optional:
// 通過靜態(tài)工廠方法 Optional.empty(),創(chuàng)建一個空的 Optional 對象 Optional<Student> optStudent = Optional.empty();
2.2.2、Optional.of(T t)方法
使用 Optional.of(T t)
方法創(chuàng)建一個包含非空值的 Optional 對象 (不推薦):
// 靜態(tài)工廠方法 Optional.of(T t),依據(jù)一個非空值創(chuàng)建一個 Optional 對象 Optional<Student> optStudent = Optional.of(student);
如果 student 為 null,這段代碼會立即拋出一個 NullPointerException,而不是等到訪問 student 的屬性值時才返回一個錯誤。
2.2.3、Optional.ofNullable(T t)方法
使用 Optional.ofNullable(T t)
方法創(chuàng)建一個包含可能為空的值的 Optional 對象 (推薦):
// 用靜態(tài)工廠方法 Optional.ofNullable(T t),你可以創(chuàng)建一個允許 null 值的 Optional 對象 Optional<Student> optStudent = Optional.ofNullable(student);
2.3、Optional對象獲取
2.3.1、get()方法
get()
方法,如果變量存在,它直接返回封裝的變量值,否則就拋出一個 NoSuchElementException 異常,不推薦使用:
optional.map(Student::getAge).get()
2.3.2、orElse(T other)方法
orElse(T other)
方法,它允許你在 Optional 對象不包含值時提供一個默認值:
optional.map(Student::getAge).orElse(20));
2.3.3、orElseGet(Supplier<? extends T> other)方法
orElseGet(Supplier<? extends T> other)
方法,它是 orElse 方法的延遲調(diào)用版,Supplier 方法只有在 Optional 對象不含值時才執(zhí)行調(diào)用(懶加載):
optional.map(Student::getAge).orElseGet(() -> Integer.MAX_VALUE);
2.3.4、orElseThrow(Supplier<? extends X> exceptionSupplier)方法
orElseThrow(Supplier<? extends X> exceptionSupplier)
方法,它和 get 方法非常類似,它們遭遇 Optional 對象為空時都會拋出一個異常,但是使用 orElseThrow 可以定制希望拋出的異常類型:
optional.orElseThrow(() -> new RuntimeException("student不存在!"));
2.3.5、ifPresent(Consumer<? super T> consumer)方法
ifPresent(Consumer<? super T> consumer)
方法,它讓能在變量值存在時執(zhí)行一個作為參數(shù)傳入的方法,否則就不進行任何操作:
optional.ifPresent(o -> o.setAge(18));
2.4、Optional對象中值的提取和轉(zhuǎn)換
2.4.1、map()方法
map()
方法,如果值存在,就對該值執(zhí)行提供的 mapping 函數(shù)調(diào)用,如果值不存在,則返回一個空的 Optional 對象。
引入 Optional 以前:
String name = null; if(insurance != null){ name = insurance.getName(); }
引入 Optional 以后:
Optional<String> name = Optional.ofNullable(insurance).map(Insurance::getName);
Optional 的 map 方法和 Java 8 中 Stream 的 map 方法相差無幾。
2.4.2、flatMap()方法
flatMap()
方法,對于嵌套式的 Optiona 結(jié)構(gòu),我們應(yīng)該使用 flatMap 方法,將兩層的 Optional 合并成一個。
我們試著重構(gòu)以下代碼:
public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); }
由于我們剛剛學習了如何使用 map,我們的第一反應(yīng)可能是我們可以利用 map 重寫之前的代碼:
Optional<Person> optPerson = Optional.of(person); Optional<String> name = optPerson.map(Person::getCar) .map(Car::getInsurance) .map(Insurance::getName);
不幸的是,這段代碼無法通過編譯。為什么呢? optPerson 是 Optional<Person>
類型的 變量, 調(diào)用 map 方法應(yīng)該沒有問題。但 getCar 返回的是一個 Optional<Car>
類型的對象,這意味著 map 操作的結(jié)果是一個 Optional<Optional<Car>>
類型的對象。因此,它對 getInsurance 的調(diào)用是非法的。
下面應(yīng)用 map 和 flatMap 對上述示例進行重寫:
public String getCarInsuranceName(Optional<Person> person) { return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse("Unknown"); // 如果Optional的結(jié)果 值為空設(shè)置默認值 }
2.5、Optional對象其他方法
2.5.1、isPresent()方法
可以使用 isPresent()
方法檢查 Optional 對象是否包含非空值,例如:
Optional<String> optional = Optional.of("Hello World"); if (optional.isPresent()) { System.out.println(optional.get()); }
2.5.2、filter()方法
filter()
方法接受一個謂詞作為參數(shù)。如果 Optional 對象的值存在,并且它符合謂詞的條件,filter 方法就返回其值,否則它就返回一個空的 Optional 對象。
比如,你可能需要檢查保險公司的名稱是否為 “Cambridge-Insurance”。
Insurance insurance = ...; if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){ System.out.println("ok"); }
使用 Optional 對象的 filter 方法,這段代碼可以重構(gòu)如下:
Optional<Insurance> optInsurance = ...; optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName())) .ifPresent(x -> System.out.println("ok"));
3、Optional注意事項
3.1、Optional的序列化問題
由于 Optiona l類設(shè)計時就沒特別考慮將其作為類的字段使用,所以它也并未實現(xiàn) Serializable 接口。由于這個原因,如果你的應(yīng)用使用了某些要求序列化的庫或者框架,在域模型中使用Optional,有可能引發(fā)應(yīng)用程序故障。
然而,我們相信,通過前面的介紹,我們已經(jīng)看到用 Optional 聲明域模型中的某些類型是個不錯的主意,尤其是你需要遍歷有可能全部或部分為空,或者可能不存在的對象時。如果你一定要實現(xiàn)序列化的域模型,作為替代方案, 我們建議你像下面這個例子那樣,提供一個能訪問聲明為 Optional、變量值可能缺失的接口,代碼清單如下:
public class Person { private Car car; public Optional<Car> getCarAsOptional() { return Optional.ofNullable(car); } }
3.2、避免使用基礎(chǔ)類型的 Optional 對象
Optional 提供了的一些基礎(chǔ)類型 —— OptionalInt
、OptionalLong
以及 OptionalDouble
,但不推薦大家使用基礎(chǔ)類型的 Optional,因為基礎(chǔ)類型的 Optional 不支持 map、 flatMap 以及 filter 方法,而這些卻是 Optional 類常用的方法??梢允褂?Optional<Int>
, Optional<Long>
, Optional<Double>
等替代。3. orElse方法的使用
3.3、orElse方法的使用
orElse 中調(diào)用的方法一直都會被執(zhí)行,orElseGet 方法只有在 Optional 對象不含值時才會被調(diào)用,所以使用 orElse 方法時需要謹慎, 以免誤執(zhí)行某些不被預(yù)期的操作。此種情況下,可使用 orElseGet 方法代替它。
總結(jié)
到此這篇關(guān)于Java新特性之Optional類超詳細介紹的文章就介紹到這了,更多相關(guān)Java新特性O(shè)ptional類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java如何將二維數(shù)組轉(zhuǎn)化為一維數(shù)組
這篇文章主要介紹了Java如何將二維數(shù)組轉(zhuǎn)化為一維數(shù)組,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08Java 在Excel中添加分離型餅圖、環(huán)形圖的方法
這篇文章主要介紹了Java 在Excel中添加分離型餅圖、環(huán)形圖的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12Java非靜態(tài)成員變量之死循環(huán)(詳解)
下面小編就為大家?guī)硪黄狫ava非靜態(tài)成員變量之死循環(huán)(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09Spring MVC 自定義數(shù)據(jù)轉(zhuǎn)換器的思路案例詳解
本文通過兩個案例來介紹下Spring MVC 自定義數(shù)據(jù)轉(zhuǎn)換器的相關(guān)知識,每種方法通過實例圖文相結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下2021-09-09Java虛擬機JVM之server模式與client模式的區(qū)別
這篇文章主要介紹了Java虛擬機JVM的client模式和Server模式兩者的區(qū)別和聯(lián)系2017-12-12