java封裝空值建議使用Optional替代null的方法示例解析
導(dǎo)讀
"Null 很糟糕." - Doug Lea。
Doug Lea 是一位美國的計算機科學(xué)家,他是 Java 平臺的并發(fā)和集合框架的主要設(shè)計者之一。他在 2014 年的一篇文章中說過:“Null sucks.”1,意思是 null 很糟糕。他認(rèn)為 null 是一種不明確的表示,它既可以表示一個值不存在,也可以表示一個值未知,也可以表示一個值無效。這樣就會導(dǎo)致很多邏輯錯誤和空指針異常,給程序員帶來很多麻煩。他建議使用 Optional 類來封裝可能為空的值,從而提高代碼的可讀性和健壯性。
"發(fā)明 null 引用是我的十億美元錯誤。" - Sir C. A. R. Hoare。
Sir C. A. R. Hoare 是一位英國的計算機科學(xué)家,他是快速排序算法、Hoare 邏輯和通信順序進(jìn)程等重要概念的發(fā)明者。他在 2009 年的一個軟件會議上道歉說:“I call it my billion-dollar mistake. It was the invention of the null reference in 1965.”,意思是他把 null 引用稱為他的十億美元錯誤。他說他在 1965 年設(shè)計 ALGOL W 語言時,引入了 null 引用的概念,用來表示一個對象變量沒有指向任何對象。他當(dāng)時認(rèn)為這是一個很簡單和自然的想法,但后來發(fā)現(xiàn)這是一個非常糟糕的設(shè)計,因為它導(dǎo)致了無數(shù)的錯誤、漏洞和系統(tǒng)崩潰。他說他應(yīng)該使用一個特殊的對象來表示空值,而不是使用 null。
自作者從事 Java 編程一來,就與 null 引用相伴,與 NullPointerException 相遇已經(jīng)是家常便飯了。
null 引用是一種表示一個對象變量沒有指向任何對象的方式,它是 Java 語言中的一個特殊值,也是導(dǎo)致空指針異常(NullPointerException)的主要原因。雖然 null 引用可以用來表示一個值不存在或未知,也可以用來節(jié)省內(nèi)存空間。但是它也不符合面向?qū)ο蟮乃枷耄驗樗皇且粋€對象,不能調(diào)用任何方法或?qū)傩浴?/p>
可以看到,null 引用并不好,我們應(yīng)該盡量避免使用 null,那么我們該怎么避免 null 引用引起的邏輯錯誤和運行時異常嘞?
其實這個問題 Java 的設(shè)計者也知道,于是他們在 Java8 之后設(shè)計引入了 Optional 類解決這個問題,本文將給大家詳細(xì)介紹下 Optional 類的設(shè)計目的以及使用方法。
Optional 類是什么?
Optional 類是 java 8 中引入的一個新的類,它的作用是封裝一個可能為空的值,從而避免空指針異常(NullPointerException)。Optional 類可以看作是一個容器,它可以包含一個非空的值,也可以為空。Optional 類提供了一些方法,讓我們可以更方便地處理可能為空的值,而不需要顯式地進(jìn)行空值檢查或者使用 null。
Optional 類的設(shè)計
Optional 類的設(shè)計是基于函數(shù)式編程的思想,它借鑒了 Scala 和 Haskell 等語言中的 Option 類型。Optional 類實現(xiàn)了 java.util.function 包中的 Supplier、Consumer、Predicate、Function 等接口,這使得它可以和 lambda 表達(dá)式或者方法引用一起使用,形成更簡潔和優(yōu)雅的代碼。
Optional 類被 final 修飾,因此它是一個不可變的類,它有兩個靜態(tài)方法用于創(chuàng)建 Optional 對象。
Optional.empty()
Optional.empty 表示一個空的 Optional 對象,它不包含任何值。
// 創(chuàng)建一個空的 Optional 對象 Optional<String> empty = Optional.empty();
Optional.of(T value)
Optional.of 表示一個非空的 Optional 對象,它包含一個非空的值。
// 創(chuàng)建一個非空的 Optional 對象 Optional<String> hello = Optional.of("Hello");
Optional.ofNullable(T value)
注意,如果我們使用 Optional.of 方法傳入一個 null 值,會拋出 NullPointerException。如果我們不確定一個值是否為空,可以使用 Optional.ofNullable 方法,它會根據(jù)值是否為空,返回一個相應(yīng)的 Optional 對象。例如:
// 創(chuàng)建一個可能為空的 Optional 對象 Optional<String> name = Optional.ofNullable("Hello");
Optional 對象的使用方法
Optional 對象提供了一些方法,讓我們可以更方便地處理可能為空的值,而不需要顯式地進(jìn)行空值檢查或者使用 null。以下是一些常用的方法。
isPresent()
判斷 Optional 對象是否包含一個非空的值,返回一個布爾值。
get()
如果 Optional 對象包含一個非空的值,返回該值,否則拋出 NoSuchElementException 異常。
// 使用 isPresent 和 get 方法 Optional<String> name = Optional.ofNullable("tom"); if (name.isPresent()) { System.out.println("Hello, " + name.get()); } else { System.out.println("Name is not available"); } // 輸出:Hello tom
ifPresent(Consumer<? super T> action)
如果 Optional 對象包含一個非空的值,執(zhí)行給定的消費者操作,否則什么也不做。
// 使用 ifPresent(Consumer<? super T> action) Optional<String> name = Optional.ofNullable("tom"); name.ifPresent(s -> { System.out.println("Hello, " + name.get()); }); // 輸出:Hello tom
orElse(T other)
如果 Optional 對象包含一個非空的值,返回該值,否則返回給定的默認(rèn)值。
// 使用 orElse(T other) Optional<String> name = Optional.ofNullable(null); String greeting = "Hello, " + name.orElse("Guest"); System.out.println(greeting); // 輸出:Hello Guest
orElseGet(Supplier<? extends T> supplier)
如果 Optional 對象包含一個非空的值,返回該值,否則返回由給定的供應(yīng)者操作生成的值。
// 使用 orElseGet(Supplier<? extends T> supplier) Optional<String> name = Optional.ofNullable(null); String greeting = "Hello, " + name.orElseGet(() -> "Guset"); System.out.println(greeting); // 輸出:Hello Guset
orElseThrow(Supplier<? extends X> exceptionSupplier)
如果 Optional 對象包含一個非空的值,返回該值,否則拋出由給定的異常供應(yīng)者操作生成的異常。
// 使用 orElseThrow(Supplier<? extends X> exceptionSupplier) Optional<String> name = Optional.ofNullable(null); String greeting = "Hello, " + name.orElseThrow(() -> new NullPointerException("null")); // 拋出 java.lang.NullPointerException: null 異常
map(Function<? super T,? extends U> mapper)
如果 Optional 對象包含一個非空的值,對該值應(yīng)用給定的映射函數(shù),返回一個包含映射結(jié)果的 Optional 對象,否則返回一個空的 Optional 對象。
// 使用 map(Function<? super T,? extends U> mapper) Optional<String> name = Optional.ofNullable("tom"); String greeting = "Hello, " + name.map(s -> s.toUpperCase()).get(); System.out.println(greeting); // 輸出:Hello TOM
flatMap(Function<? super T,Optional<U>> mapper)
如果 Optional 對象包含一個非空的值,對該值進(jìn)行 mapper 參數(shù)操作,返回新的 Optional 對象,否則返回一個空的 Optional 對象。
// 使用 flatMap(Function<? super T,Optional<U>> mapper) Optional<String> name = Optional.ofNullable("tom"); String greeting = name.flatMap(s -> Optional.of("Hello " + s)).get(); System.out.println(greeting); // 輸出:Hello tom
filter(Predicate<? super T> predicate)
如果 Optional 對象包含一個非空的值,并且該值滿足給定的謂詞條件,返回包含該值的 Optional 對象,否則返回一個空的 Optional 對象。
// filter(Predicate<? super T> predicate) Optional<String> name = Optional.ofNullable("tom"); String greeting = "Hello " + name.filter(s -> !s.isEmpty()).get(); System.out.println(greeting); // 輸出:Hello tom
Java 9 中 Optional 改進(jìn)
Java 9 中 Optional 類有了一些改進(jìn),主要是增加了三個新的方法,分別是 stream()、ifPresentOrElse() 和 or()。這些方法可以讓我們更方便地處理可能為空的值,以及和流或其他返回 Optional 的方法結(jié)合使用。我來詳細(xì)講解一下這些方法的作用和用法。
stream()
這個方法可以將一個 Optional 對象轉(zhuǎn)換為一個 Stream 對象,如果 Optional 對象包含一個非空的值,那么返回的 Stream 對象就包含這個值,否則返回一個空的 Stream 對象。這樣我們就可以利用 Stream 的各種操作來處理 Optional 的值,而不需要顯式地判斷是否為空。我們可以用 stream() 方法來過濾一個包含 Optional 的列表,只保留非空的值,如下所示:
List<Optional<String>> list = Arrays.asList( Optional.empty(), Optional.of("A"), Optional.empty(), Optional.of("B") ); // 使用 stream() 方法過濾列表,只保留非空的值 List<String> filteredList = list.stream() .flatMap(Optional::stream) .collect(Collectors.toList()); System.out.println(filteredList); // 輸出 [A, B]
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
這個方法可以讓我們在 Optional 對象包含值或者為空時,執(zhí)行不同的操作。它接受兩個參數(shù),一個是 Consumer 類型的 action,一個是 Runnable 類型的 emptyAction。如果 Optional 對象包含一個非空的值,那么就執(zhí)行 action.accept(value),如果 Optional 對象為空,那么就執(zhí)行 emptyAction.run()。這樣我們就可以避免使用 if-else 語句來判斷 Optional 是否為空,而是使用函數(shù)式編程的方式來處理不同的情況。我們可以用 ifPresentOrElse() 方法來打印 Optional 的值,或者提示不可用,如下所示 :
Optional<Integer> optional = Optional.of(1); optional.ifPresentOrElse( x -> System.out.println("Value: " + x), () -> System.out.println("Not Present.") ); optional = Optional.empty(); optional.ifPresentOrElse( x -> System.out.println("Value: " + x), () -> System.out.println("Not Present.") ); // 輸出:Value: 1 // 輸出:Not Present.
or(Supplier<? extends Optional<? extends T>> supplier)
這個方法可以讓我們在 Optional 對象為空時,返回一個預(yù)設(shè)的值。它接受一個 Supplier 類型的 supplier,如果 Optional 對象包含一個非空的值,那么就返回這個 Optional 對象本身,如果 Optional 對象為空,那么就返回 supplier.get() 返回的 Optional 對象。這樣我們就可以避免使用三元運算符或者其他方式來設(shè)置默認(rèn)值,而是使用函數(shù)式編程的方式來提供備選值。我們可以用 or() 方法來設(shè)置 Optional 的默認(rèn)值,如下所示:
Optional<String> optional = Optional.of("Hello "); Supplier<Optional<String>> supplier = () -> Optional.of("tom"); optional = optional.or(supplier); optional.ifPresent(x -> System.out.println(x)); optional = Optional.empty(); optional = optional.or(supplier); optional.ifPresent(x -> System.out.println(x)); // 輸出:Hello // 輸出:tom
為什么我推薦你使用 Optional 類
最后我總結(jié)一下使用 Optional 類的幾個好處:
- 可以避免空指針異常,提高代碼的健壯性和可讀性。
- 可以減少顯式的空值檢查和 null 的使用,使代碼更簡潔和優(yōu)雅。
- 可以利用函數(shù)式編程的特性,實現(xiàn)更靈活和高效的邏輯處理。
- 可以提高代碼的可測試性,方便進(jìn)行單元測試和集成測試。
總之,Optional 類是一個非常有用的類,它可以幫助我們更好地處理可能為空的值,提高代碼的質(zhì)量和效率。所以我強烈推薦你在 Java 開發(fā)中使用 Optional 類,你會發(fā)現(xiàn)它的魅力和好處。
以上就是java封裝空值建議使用Optional替代null的方法示例解析的詳細(xì)內(nèi)容,更多關(guān)于java Optional封裝空值的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實現(xiàn)經(jīng)典游戲2048的示例代碼
2014年Gabriele Cirulli利用周末的時間寫2048這個游戲的程序。本文將用java語言實現(xiàn)這一經(jīng)典游戲,并采用了swing技術(shù)進(jìn)行了界面化處理,需要的可以參考一下2022-02-02解決springboot項目上傳文件出現(xiàn)臨時文件目錄為空的問題
這篇文章主要介紹了解決springboot項目上傳文件出現(xiàn)臨時文件目錄為空的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09java開發(fā)中基于JDBC連接數(shù)據(jù)庫實例總結(jié)
這篇文章主要介紹了java開發(fā)中基于JDBC連接數(shù)據(jù)庫的方法,以實例形式較為詳細(xì)的總結(jié)分析了Java使用JDBC的具體步驟與注意事項,并附帶了一個完整實例加以說明,需要的朋友可以參考下2015-11-11Spring Boot 自定義 Shiro 過濾器無法使用 @Autowired問題及解決方法
這篇文章主要介紹了Spring Boot 自定義 Shiro 過濾器無法使用 @Autowired問題及解決方法 ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-06-06Springboot項目的搭建教程(分離出common父依賴)
這篇文章主要介紹了Springboot項目的搭建教程(分離出common父依賴),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01