JDK8新出Optional類的方法探索與思考分析
引言
所有的 Java 程序員基本都會(huì)遇到 NullPointerException 異常,一般處理這個(gè)問題可能不會(huì)是很難,但是有時(shí)去排查到底是哪引起的會(huì)耗費(fèi)很長時(shí)間很是麻煩。最近了解到 JDK1.8 新增加了一個(gè) Optional 類可以避免一些 NullPointerException 異常,下面讓我們一起去了解一下它吧。
基于值的類(Value-based Classes)
有些類,如 java.util.Optional 和 java.time.LocalDateTime,是基于值的。基于值的類的實(shí)例:
- 是最終的和不可變的(盡管可能包含對(duì)可變對(duì)象的引用);
- 具有 equals、hashCode 和 toString 的實(shí)現(xiàn),這些實(shí)現(xiàn)僅根據(jù)實(shí)例的狀態(tài)計(jì)算,而不是根據(jù)實(shí)例的標(biāo)識(shí)或任何其他對(duì)象或變量的狀態(tài)計(jì)算;
- 不使用對(duì)身份敏感的操作,例如實(shí)例之間的引用相等(==)、實(shí)例的身份哈希代碼或?qū)?shí)例的內(nèi)部鎖進(jìn)行同步;
- 僅基于 equals() 而不是基于引用相等 (==) 被視為相等;
- 沒有可訪問的構(gòu)造函數(shù),而是通過工廠方法進(jìn)行實(shí)例化,這些方法不提交返回實(shí)例的標(biāo)識(shí);
- 當(dāng)相等時(shí)是可自由替換的,這意味著在任何計(jì)算或方法調(diào)用中,根據(jù) equals() 交換任何兩個(gè)相等的實(shí)例 x 和 y 都不會(huì)產(chǎn)生明顯的行為變化。
如果程序試圖區(qū)分對(duì)基于值的類的相等值的兩個(gè)引用,無論是直接通過引用相等,還是間接通過調(diào)用同步、身份哈希、序列化或任何其他身份敏感機(jī)制,都可能產(chǎn)生不可預(yù)測的結(jié)果。在基于值的類的實(shí)例上使用這種對(duì)身份敏感的操作可能會(huì)產(chǎn)生不可預(yù)測的影響,應(yīng)該避免。
簡單地說,基于值的類的實(shí)例是最終的,不可變的,并且這些實(shí)例沒有適當(dāng)?shù)臓顟B(tài)和標(biāo)識(shí),因此某些操作是特定于標(biāo)識(shí)的,因此不應(yīng)使用。
一、Optional中的基本方法
Optional 類位于 java.util 包下,它是一個(gè)容器對(duì)象,可能包含也可能不包含非空值。
這是一個(gè)基于值的類;在Optional實(shí)例上使用身份敏感操作(包括引用相等(==)、身份哈希碼或同步)可能會(huì)產(chǎn)生不可預(yù)測的結(jié)果,應(yīng)該避免。
1、創(chuàng)建方法
empty()
返回一個(gè)空的 Optional 實(shí)例
public class Main { public static void main(String[] args) { System.out.println(Optional); } } // 輸出 Optional.empty
注意:不要通過與Option.empty()返回的實(shí)例進(jìn)行==比較來避免測試對(duì)象是否為空,因?yàn)椴荒鼙WC它是單例的。
of(T value)
返回一個(gè)帶值的 Optional,如果 value 是 null 會(huì)拋出 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.empty
2、判斷方法
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)用指定的消費(fèi)者,否則什么都不做。
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()
如果這個(gè) Optional 中存在一個(gè)值,則返回該值,否則拋出 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)
如果存在一個(gè)值,并且該值與給定的過濾條件匹配,則返回一個(gè)描述該值的 Optional,否則返回一個(gè)空的 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.empty
map(Function<? super T, ? extends U> mapper)
如果存在值,則將提供的映射函數(shù)應(yīng)用于該值,如果結(jié)果為非 null,則返回一個(gè)描述結(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.empty
flatMap(Function<? super T, Optionalu> mapper)
如果存在值,請(qǐng)將提供的 Optional 方位映射函數(shù)應(yīng)用于該值,返回該結(jié)果,否則返回空的 Optional。此方法類似于map(Function),但提供的 mapper 的結(jié)果已經(jīng)是 Optional,并且如果調(diào)用,flatMap 不會(huì)用額外的 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]
這個(gè)例子中體現(xiàn)出了 map 和 flatMap 方法的區(qū)別,map 方法會(huì)在返回時(shí)用 Optional 進(jìn)行包裝而 flatMap 方法不會(huì)再進(jìn)行額外的包裝。
orElse(T other)
如果存在這個(gè)值則返回這個(gè)值,否則返回傳入的值 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 empty
orElseGet(Supplier<? extends T> other)
如果存在這個(gè)值則返回這個(gè)值,否則調(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 emanjusaka
orElseThrow(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 方法會(huì)在返回時(shí)用 Optional 進(jìn)行包裝而 flatMap 方法不會(huì)再進(jìn)行額外的包裝。
2、orElse 和 orElseGet 方法的區(qū)別
- orElse():如果有值則將其返回,否則返回指定的 other。
- orElseGet():如果有值則將其返回,否則調(diào)用函數(shù) other 并將其返回調(diào)用結(jié)果。
- orElse() 方法在 Optional 值為非空時(shí),也會(huì)計(jì)算傳入的參數(shù),而 orElseGet() 方法只有在 Optional 值為空時(shí)才會(huì)執(zhí)行傳入的函數(shù)。
如果是傳值可以選用 orElse(),如果傳入的是方法選用orElseGet()??。
三、總結(jié)
在使用 Optional 時(shí)我覺得應(yīng)該盡量避免一些情況:
- 永遠(yuǎn)不要通過返回 Optional 的方法返回一個(gè)空值:它破壞 Optional 設(shè)計(jì)的初衷。
- 并不是所有的返回類型都能從 Optional 的處理中獲益。容器類型,包括集合、映射、Stream、數(shù)組和 Optional,不應(yīng)該封裝在 Optional 中。與其返回一個(gè)空的 Optional<List<T>> ,不還如返回一個(gè) 空的 List<T> 。
- 除了「次要基本類型(minor primitive types)」Boolean,Byte,Character,Short 和 Float 之外,永遠(yuǎn)不應(yīng)該返回裝箱的基本類型 的 Optional。
總之,如果發(fā)現(xiàn)自己編寫的方法不能總是返回值,并且認(rèn)為該方法的用戶在每次調(diào)用時(shí)考慮這種可能性很重要,那么或許應(yīng)該返回一個(gè) Optional 的方法。但是,應(yīng)該意識(shí)到,返回 Optional 會(huì)帶來實(shí)際的性能后果;對(duì)于性能關(guān)鍵的方法,最好返回 null 或拋出異常。最后,除了作為返回值之外,不應(yīng)該在任何其他地方中使用 Optional。
使用 Optional 時(shí)要注意,我認(rèn)為它并不能完全避免空指針。如果這個(gè)值是 null ,不做額外的判斷,直接使用還是會(huì)有空指針的問題。使用 Optional 的好處是它可以幫助我們簡化判空的操作,簡潔我們的代碼。用了 Optional 最后拿結(jié)果的時(shí)候還是要小心的,盲目 get 一樣會(huì)拋錯(cuò)。
參考文獻(xiàn)
《Effective Java》jdk11的文檔
以上就是JDK8新出Optional類的方法探索與思考分析的詳細(xì)內(nèi)容,更多關(guān)于JDK8 Optional類的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢實(shí)例詳解
MyBatis-Plus是一個(gè)MyBatis(opens new window)的增強(qiáng)工具,在 MyBatis的基礎(chǔ)上只做增強(qiáng)不做改變,MyBatis可以無損升級(jí)為MyBatis-Plus,這篇文章主要給大家介紹了關(guān)于mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢的相關(guān)資料,需要的朋友可以參考下2022-10-10IDEA新建javaWeb以及Servlet簡單實(shí)現(xiàn)小結(jié)
這篇文章主要介紹了IDEA新建javaWeb以及Servlet簡單實(shí)現(xiàn)小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11Java在Excel中創(chuàng)建多級(jí)分組、折疊或展開分組的實(shí)現(xiàn)
這篇文章主要介紹了Java在Excel中創(chuàng)建多級(jí)分組、折疊或展開分組的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05