JDK8新出Optional類的方法探索與思考分析
引言
所有的 Java 程序員基本都會遇到 NullPointerException 異常,一般處理這個問題可能不會是很難,但是有時去排查到底是哪引起的會耗費很長時間很是麻煩。最近了解到 JDK1.8 新增加了一個 Optional 類可以避免一些 NullPointerException 異常,下面讓我們一起去了解一下它吧。
基于值的類(Value-based Classes)
有些類,如 java.util.Optional 和 java.time.LocalDateTime,是基于值的?;谥档念惖膶嵗?/p>
- 是最終的和不可變的(盡管可能包含對可變對象的引用);
- 具有 equals、hashCode 和 toString 的實現(xiàn),這些實現(xiàn)僅根據(jù)實例的狀態(tài)計算,而不是根據(jù)實例的標識或任何其他對象或變量的狀態(tài)計算;
- 不使用對身份敏感的操作,例如實例之間的引用相等(==)、實例的身份哈希代碼或?qū)嵗膬?nèi)部鎖進行同步;
- 僅基于 equals() 而不是基于引用相等 (==) 被視為相等;
- 沒有可訪問的構(gòu)造函數(shù),而是通過工廠方法進行實例化,這些方法不提交返回實例的標識;
- 當相等時是可自由替換的,這意味著在任何計算或方法調(diào)用中,根據(jù) equals() 交換任何兩個相等的實例 x 和 y 都不會產(chǎn)生明顯的行為變化。
如果程序試圖區(qū)分對基于值的類的相等值的兩個引用,無論是直接通過引用相等,還是間接通過調(diào)用同步、身份哈希、序列化或任何其他身份敏感機制,都可能產(chǎn)生不可預(yù)測的結(jié)果。在基于值的類的實例上使用這種對身份敏感的操作可能會產(chǎn)生不可預(yù)測的影響,應(yīng)該避免。
簡單地說,基于值的類的實例是最終的,不可變的,并且這些實例沒有適當?shù)臓顟B(tài)和標識,因此某些操作是特定于標識的,因此不應(yīng)使用。
一、Optional中的基本方法
Optional 類位于 java.util 包下,它是一個容器對象,可能包含也可能不包含非空值。
這是一個基于值的類;在Optional實例上使用身份敏感操作(包括引用相等(==)、身份哈希碼或同步)可能會產(chǎn)生不可預(yù)測的結(jié)果,應(yīng)該避免。
1、創(chuàng)建方法
empty()
返回一個空的 Optional 實例
public class Main {
public static void main(String[] args) {
System.out.println(Optional);
}
}
// 輸出
Optional.empty注意:不要通過與Option.empty()返回的實例進行==比較來避免測試對象是否為空,因為不能保證它是單例的。
of(T value)
返回一個帶值的 Optional,如果 value 是 null 會拋出 NullPointerException 異常
public static void main(String[] args) {
Optional<String> emanjusaka = Optional.of("emanjusaka");
System.out.println(emanjusaka);
}
// 輸出
Optional[emanjusaka]ofNullable(T value)
如果非空,返回描述指定值的 Optional,否則返回空 Optional。
public static void main(String[] args) {
Optional<String> emanjusaka = Optional.ofNullable("emanjusaka");
System.out.println(emanjusaka);
Optional<String> empty = Optional.ofNullable(null);
System.out.println(empty);
}
// 輸出
Optional[emanjusaka]
Optional.empty2、判斷方法
isPresent()
如果存在值則返回 true,否則返回 false。
public static void main(String[] args) {
Optional<String> emanjusaka = Optional.ofNullable("emanjusaka");
System.out.println(emanjusaka);
System.out.println(emanjusaka.isPresent());
Optional<String> empty = Optional.ofNullable(null);
System.out.println(empty);
System.out.println(empty.isPresent());
}
//輸出
Optional[emanjusaka]
true
Optional.empty
false?ifPresent(Consumer<? super T> consumer)??
如果存在值,則使用該值調(diào)用指定的消費者,否則什么都不做。
public static void main(String[] args) {
Optional<String> emanjusaka = Optional.of("emanjusaka");
Consumer<String> consumer = s -> {
s = "hello " + s;
System.out.println(s);
};
System.out.println(emanjusaka);
emanjusaka.ifPresent(consumer);
}
// 輸出
Optional[emanjusaka]
hello emanjusaka一般用于判斷 Optional 是否為空,并在不為空的情況下執(zhí)行相應(yīng)的操作。
3、獲取方法
get()
如果這個 Optional 中存在一個值,則返回該值,否則拋出 NoSuchElementException。
public static void main(String[] args) {
Optional<String> emanjusaka = Optional.ofNullable("emanjusaka");
System.out.println(emanjusaka);
System.out.println(emanjusaka.get());
Optional<String> empty = Optional.ofNullable(null);
System.out.println(empty);
System.out.println(empty.get());
}
// 輸出
Optional[emanjusaka]
emanjusaka
Optional.empty
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at org.example.Main.main(Main.java:13)filter(Predicate<? super T> predicate)
如果存在一個值,并且該值與給定的過濾條件匹配,則返回一個描述該值的 Optional,否則返回一個空的 Optional。
public static void main(String[] args) {
Optional<String> emanjusaka = Optional.of("emanjusaka");
System.out.println(emanjusaka);
System.out.println(emanjusaka.filter(s -> {
return "emanjusaka".equals(s);
}));
System.out.println(emanjusaka.filter(s -> false));
}
// 輸出
Optional[emanjusaka]
Optional[emanjusaka]
Optional.emptymap(Function<? super T, ? extends U> mapper)
如果存在值,則將提供的映射函數(shù)應(yīng)用于該值,如果結(jié)果為非 null,則返回一個描述結(jié)果的 Optional。否則返回空的 Optional。
public static void main(String[] args) {
User user = new User();
user.setName("emanjusaka");
Optional<User> optional = Optional.of(user);
System.out.println(optional);
System.out.println(optional.map(User::getName));
User userEmpty = new User();
Optional<User> optionalEmpty = Optional.of(userEmpty);
System.out.println(optionalEmpty);
System.out.println(optionalEmpty.map(User::getName));
}
private static class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// 輸出
Optional[org.example.Main$User@2503dbd3]
Optional[emanjusaka]
Optional[org.example.Main$User@6d03e736]
Optional.emptyflatMap(Function<? super T, Optionalu> mapper)
如果存在值,請將提供的 Optional 方位映射函數(shù)應(yīng)用于該值,返回該結(jié)果,否則返回空的 Optional。此方法類似于map(Function),但提供的 mapper 的結(jié)果已經(jīng)是 Optional,并且如果調(diào)用,flatMap 不會用額外的 Optional 包裝它。
public static void main(String[] args) {
User user = new User();
user.setName("emanjusaka");
user.setAge(Optional.of(35));
Optional<User> optional = Optional.of(user);
System.out.println(optional);
System.out.println(optional.map(User::getAge));
System.out.println(optional.flatMap(User::getAge));
}
private static class User {
private String name;
private Optional<Integer> age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Optional<Integer> getAge() {
return age;
}
public void setAge(Optional<Integer> age) {
this.age = age;
}
}
// 輸出
Optional[org.example.Main$User@2503dbd3]
Optional[Optional[35]]
Optional[35]這個例子中體現(xiàn)出了 map 和 flatMap 方法的區(qū)別,map 方法會在返回時用 Optional 進行包裝而 flatMap 方法不會再進行額外的包裝。
orElse(T other)
如果存在這個值則返回這個值,否則返回傳入的值 other
public static void main(String[] args) {
Optional<String> optional = Optional.of("emanjusaka");
System.out.println(optional);
System.out.println(optional.orElse(null));
Optional<String> optionalEmpty = Optional.ofNullable(null);
System.out.println(optionalEmpty);
System.out.println(optionalEmpty.orElse("empty"));
}
// 輸出
Optional[emanjusaka]
emanjusaka
Optional.empty
emptyorElseGet(Supplier<? extends T> other)
如果存在這個值則返回這個值,否則調(diào)用 other 并返回該調(diào)用的結(jié)果。
public static void main(String[] args) {
Optional<String> optional = Optional.of("emanjusaka");
System.out.println(optional);
System.out.println(optional.orElseGet(() -> "hello emanjusaka"));
Optional<String> optionalEmpty = Optional.ofNullable(null);
System.out.println(optionalEmpty);
System.out.println(optionalEmpty.orElseGet(() -> "hello emanjusaka"));
}
// 輸出
Optional[emanjusaka]
emanjusaka
Optional.empty
hello emanjusakaorElseThrow(Supplier? extends X exceptionSupplier)
返回包含的值(如果存在),否則拋出由所提供創(chuàng)建的異常。
public static void main(String[] args) {
Optional<String> optionalEmpty = Optional.ofNullable(null);
System.out.println(optionalEmpty);
System.out.println(optionalEmpty.orElseThrow(ArithmeticException::new));
}
// 輸出
Optional.empty
Exception in thread "main" java.lang.ArithmeticException
at java.util.Optional.orElseThrow(Optional.java:290)
at org.example.Main.main(Main.java:9)二、Optional 中方法的區(qū)別
1、map 和 flatMap 方法的區(qū)別
- map 方法會在返回時用 Optional 進行包裝而 flatMap 方法不會再進行額外的包裝。
2、orElse 和 orElseGet 方法的區(qū)別
- orElse():如果有值則將其返回,否則返回指定的 other。
- orElseGet():如果有值則將其返回,否則調(diào)用函數(shù) other 并將其返回調(diào)用結(jié)果。
- orElse() 方法在 Optional 值為非空時,也會計算傳入的參數(shù),而 orElseGet() 方法只有在 Optional 值為空時才會執(zhí)行傳入的函數(shù)。
如果是傳值可以選用 orElse(),如果傳入的是方法選用orElseGet()??。
三、總結(jié)
在使用 Optional 時我覺得應(yīng)該盡量避免一些情況:
- 永遠不要通過返回 Optional 的方法返回一個空值:它破壞 Optional 設(shè)計的初衷。
- 并不是所有的返回類型都能從 Optional 的處理中獲益。容器類型,包括集合、映射、Stream、數(shù)組和 Optional,不應(yīng)該封裝在 Optional 中。與其返回一個空的 Optional<List<T>> ,不還如返回一個 空的 List<T> 。
- 除了「次要基本類型(minor primitive types)」Boolean,Byte,Character,Short 和 Float 之外,永遠不應(yīng)該返回裝箱的基本類型 的 Optional。
總之,如果發(fā)現(xiàn)自己編寫的方法不能總是返回值,并且認為該方法的用戶在每次調(diào)用時考慮這種可能性很重要,那么或許應(yīng)該返回一個 Optional 的方法。但是,應(yīng)該意識到,返回 Optional 會帶來實際的性能后果;對于性能關(guān)鍵的方法,最好返回 null 或拋出異常。最后,除了作為返回值之外,不應(yīng)該在任何其他地方中使用 Optional。
使用 Optional 時要注意,我認為它并不能完全避免空指針。如果這個值是 null ,不做額外的判斷,直接使用還是會有空指針的問題。使用 Optional 的好處是它可以幫助我們簡化判空的操作,簡潔我們的代碼。用了 Optional 最后拿結(jié)果的時候還是要小心的,盲目 get 一樣會拋錯。
參考文獻
《Effective Java》jdk11的文檔
以上就是JDK8新出Optional類的方法探索與思考分析的詳細內(nèi)容,更多關(guān)于JDK8 Optional類的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
maven插件spring-boot-starter-tomcat的使用方式
這篇文章主要介紹了maven插件spring-boot-starter-tomcat的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
SpringBoot中JSON工具類JsonUtils的實踐教學
Spring?Boot?項目里,JSON?工具類是每個?Service?都要寫的東西,JsonUtils的核心是一個統(tǒng)一接口覆蓋三套?JSON?引擎,本文主要介紹了JsonUtils在SpringBoot項目中的具體應(yīng)用,希望對大家有所幫助2026-06-06
RocketMQ線程池創(chuàng)建實現(xiàn)原理詳解
這篇文章主要為大家介紹了RocketMQ線程池創(chuàng)建實現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
grade構(gòu)建閱讀spring源碼環(huán)境 Idea2020.3的過程
這篇文章主要介紹了grade構(gòu)建閱讀spring源碼環(huán)境 Idea2020.3,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
如何解決org.apache.jasper.JasperException:無法為JSP編譯類詳解
這篇文章主要給大家介紹了關(guān)于如何解決org.apache.jasper.JasperException:無法為JSP編譯類的相關(guān)資料,原因可能是JSP文件的語法錯誤、類路徑問題或其他配置問題,建議檢查JSP文件的語法、類路徑配置和其他相關(guān)配置,需要的朋友可以參考下2023-06-06
Java數(shù)據(jù)結(jié)構(gòu)之稀疏數(shù)組的實現(xiàn)與應(yīng)用
這篇文章主要為大家詳細介紹了Java數(shù)據(jù)結(jié)構(gòu)中稀疏數(shù)組的實現(xiàn)與應(yīng)用,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的可以了解一下2022-10-10

