JDK8新出Optional類的方法探索與思考分析
引言
所有的 Java 程序員基本都會(huì)遇到 NullPointerException 異常,一般處理這個(gè)問(wèn)題可能不會(huì)是很難,但是有時(shí)去排查到底是哪引起的會(huì)耗費(fèi)很長(zhǎng)時(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() 而不是基于引用相等 (==) 被視為相等;
- 沒(méi)有可訪問(wèn)的構(gòu)造函數(shù),而是通過(guò)工廠方法進(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è)引用,無(wú)論是直接通過(guò)引用相等,還是間接通過(guò)調(diào)用同步、身份哈希、序列化或任何其他身份敏感機(jī)制,都可能產(chǎn)生不可預(yù)測(cè)的結(jié)果。在基于值的類的實(shí)例上使用這種對(duì)身份敏感的操作可能會(huì)產(chǎn)生不可預(yù)測(cè)的影響,應(yīng)該避免。
簡(jiǎn)單地說(shuō),基于值的類的實(shí)例是最終的,不可變的,并且這些實(shí)例沒(méi)有適當(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ù)測(cè)的結(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注意:不要通過(guò)與Option.empty()返回的實(shí)例進(jìn)行==比較來(lái)避免測(cè)試對(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.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)用指定的消費(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è)值,并且該值與給定的過(guò)濾條件匹配,則返回一個(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.emptymap(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.emptyflatMap(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
emptyorElseGet(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 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 方法會(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í)我覺(jué)得應(yīng)該盡量避免一些情況:
- 永遠(yuǎn)不要通過(guò)返回 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)自己編寫(xiě)的方法不能總是返回值,并且認(rèn)為該方法的用戶在每次調(diào)用時(shí)考慮這種可能性很重要,那么或許應(yīng)該返回一個(gè) Optional 的方法。但是,應(yīng)該意識(shí)到,返回 Optional 會(huì)帶來(lái)實(shí)際的性能后果;對(duì)于性能關(guān)鍵的方法,最好返回 null 或拋出異常。最后,除了作為返回值之外,不應(yīng)該在任何其他地方中使用 Optional。
使用 Optional 時(shí)要注意,我認(rèn)為它并不能完全避免空指針。如果這個(gè)值是 null ,不做額外的判斷,直接使用還是會(huì)有空指針的問(wèn)題。使用 Optional 的好處是它可以幫助我們簡(jiǎn)化判空的操作,簡(jiǎn)潔我們的代碼。用了 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可以無(wú)損升級(jí)為MyBatis-Plus,這篇文章主要給大家介紹了關(guān)于mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢的相關(guān)資料,需要的朋友可以參考下2022-10-10
IDEA新建javaWeb以及Servlet簡(jiǎn)單實(shí)現(xiàn)小結(jié)
這篇文章主要介紹了IDEA新建javaWeb以及Servlet簡(jiǎn)單實(shí)現(xiàn)小結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
Java在Excel中創(chuàng)建多級(jí)分組、折疊或展開(kāi)分組的實(shí)現(xiàn)
這篇文章主要介紹了Java在Excel中創(chuàng)建多級(jí)分組、折疊或展開(kāi)分組的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05

