null 不好,我推薦你使用 Optional
"Null 很糟糕." - Doug Lea。
Doug Lea 是一位美國的電腦科學家,他是 Java 平臺的併發和集合框架的主要設計者之一。他在 2014 年的一篇文章中說過:“Null sucks.”1,意思是 null 很糟糕。他認為 null 是一種不明確的表示,它既可以表示一個值不存在,也可以表示一個值未知,也可以表示一個值無效。這樣就會導致很多邏輯錯誤和空指標異常,給程式設計師帶來很多麻煩。他建議使用 Optional 類來封裝可能為空的值,從而提高程式碼的可讀性和健壯性。
"發明 null 引用是我的十億美元錯誤。" - Sir C. A. R. Hoare。
Sir C. A. R. Hoare 是一位英國的電腦科學家,他是快速排序演算法、Hoare 邏輯和通訊順序程式等重要概念的發明者。他在 2009 年的一個軟體會議上道歉說:“I call it my billion-dollar mistake. It was the invention of the null reference in 1965.”,意思是他把 null 引用稱為他的十億美元錯誤。他說他在 1965 年設計 ALGOL W 語言時,引入了 null 引用的概念,用來表示一個物件變數沒有指向任何物件。他當時認為這是一個很簡單和自然的想法,但後來發現這是一個非常糟糕的設計,因為它導致了無數的錯誤、漏洞和系統崩潰。他說他應該使用一個特殊的物件來表示空值,而不是使用 null。
自作者從事 Java 程式設計一來,就與 null 引用相伴,與 NullPointerException 相遇已經是家常便飯了。
null 引用是一種表示一個物件變數沒有指向任何物件的方式,它是 Java 語言中的一個特殊值,也是導致空指標異常(NullPointerException)的主要原因。雖然 null 引用可以用來表示一個值不存在或未知,也可以用來節省記憶體空間。但是它也不符合物件導向的思想,因為它不是一個物件,不能呼叫任何方法或屬性。
可以看到,null 引用並不好,我們應該儘量避免使用 null,那麼我們該怎麼避免 null 引用引起的邏輯錯誤和執行時異常嘞?
其實這個問題 Java 的設計者也知道,於是他們在 Java8 之後設計引入了 Optional 類解決這個問題,本文將給大家詳細介紹下 Optional 類的設計目的以及使用方法。
Optional 類是什麼?
Optional 類是 java 8 中引入的一個新的類,它的作用是封裝一個可能為空的值,從而避免空指標異常(NullPointerException)。Optional 類可以看作是一個容器,它可以包含一個非空的值,也可以為空。Optional 類提供了一些方法,讓我們可以更方便地處理可能為空的值,而不需要顯式地進行空值檢查或者使用 null。
Optional 類的設計
Optional 類的設計是基於函數語言程式設計的思想,它借鑑了 Scala 和 Haskell 等語言中的 Option 型別。Optional 類實現了 java.util.function 包中的 Supplier、Consumer、Predicate、Function 等介面,這使得它可以和 lambda 表示式或者方法引用一起使用,形成更簡潔和優雅的程式碼。
Optional 類是一個不可變的類,它有兩個子類:Optional.empty 和 Optional.of。Optional.empty 表示一個空的 Optional 物件,它不包含任何值。Optional.of 表示一個非空的 Optional 物件,它包含一個非空的值。我們可以使用這兩個靜態方法來建立 Optional 物件。例如:
// 建立一個空的 Optional 物件
Optional<String> empty = Optional.empty();
// 建立一個非空的 Optional 物件
Optional<String> hello = Optional.of("Hello");
注意,如果我們使用 Optional.of 方法傳入一個 null 值,會丟擲 NullPointerException。如果我們不確定一個值是否為空,可以使用 Optional.ofNullable 方法,它會根據值是否為空,返回一個相應的 Optional 物件。例如:
// 建立一個可能為空的 Optional 物件
Optional<String> name = Optional.ofNullable(getName());
Optional 類的使用方法
Optional 類提供了一些方法,讓我們可以更方便地處理可能為空的值,而不需要顯式地進行空值檢查或者使用 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 物件包含一個非空的值,執行給定的消費者操作,否則什麼也不做。
// 使用 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 物件包含一個非空的值,返回該值,否則返回給定的預設值。
// 使用 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 物件包含一個非空的值,返回該值,否則返回由給定的供應者操作生成的值。
// 使用 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 物件包含一個非空的值,返回該值,否則丟擲由給定的異常供應者操作生成的異常。
// 使用 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 物件包含一個非空的值,對該值應用給定的對映函式,返回一個包含對映結果的 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 物件包含一個非空的值,對該值進行 mapper 引數操作,返回新的 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 改進
Java 9 中 Optional 類有了一些改進,主要是增加了三個新的方法,分別是 stream()、ifPresentOrElse() 和 or()。這些方法可以讓我們更方便地處理可能為空的值,以及和流或其他返回 Optional 的方法結合使用。我來詳細講解一下這些方法的作用和用法。
stream()
:這個方法可以將一個 Optional 物件轉換為一個 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()
:這個方法可以讓我們在 Optional 物件包含值或者為空時,執行不同的操作。它接受兩個引數,一個是 Consumer 型別的 action,一個是 Runnable 型別的 emptyAction。如果 Optional 物件包含一個非空的值,那麼就執行 action.accept(value),如果 Optional 物件為空,那麼就執行 emptyAction.run()。這樣我們就可以避免使用 if-else 語句來判斷 Optional 是否為空,而是使用函數語言程式設計的方式來處理不同的情況。我們可以用 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()
:這個方法可以讓我們在 Optional 物件為空時,返回一個預設的值。它接受一個 Supplier 型別的 supplier,如果 Optional 物件包含一個非空的值,那麼就返回這個 Optional 物件本身,如果 Optional 物件為空,那麼就返回 supplier.get() 返回的 Optional 物件。這樣我們就可以避免使用三元運算子或者其他方式來設定預設值,而是使用函數語言程式設計的方式來提供備選值。我們可以用 or() 方法來設定 Optional 的預設值,如下所示:
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 類的幾個好處:
可以避免空指標異常,提高程式碼的健壯性和可讀性。 可以減少顯式的空值檢查和 null 的使用,使程式碼更簡潔和優雅。 可以利用函數語言程式設計的特性,實現更靈活和高效的邏輯處理。 可以提高程式碼的可測試性,方便進行單元測試和整合測試。
總之,Optional 類是一個非常有用的類,它可以幫助我們更好地處理可能為空的值,提高程式碼的質量和效率。所以我強烈推薦你在 Java 開發中使用 Optional 類,你會發現它的魅力和好處。
·END·
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2996098/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- null 不好,我真的推薦你使用 OptionalNull
- 你的專案使用Optional了嗎?
- 《Java 8 in Action》Chapter 10:用Optional取代nullJavaAPTNull
- 使用Java8中的Optional類來消除程式碼中的null檢查JavaNull
- Maven中optional和scope元素的使用,你弄明白了?Maven
- Java Optional使用指南Java
- java8學習筆記01 Optional物件替代Null判斷Java筆記物件Null
- 《Java8實戰》-第十章筆記(用Optional取代null)Java筆記Null
- Java Optional使用的最佳實踐Java
- optional
- 在Java中使用Optional效能很慢 - pkolaczkJava
- SCSS !optionalCSS
- NULL 使用詳解Null
- springboot開發介面報錯Optional int parameter 'userId' is present but cannot be translated into a null v......Spring BootNull
- 使用Java Optional類的最佳實踐 - oracleJavaOracle
- 使用 Java 8 Optional 的正確姿勢Java
- 為什麼索引無法使用is null和is not null索引Null
- 為什麼你學不好程式設計程式設計
- ((NULL) null).printNULL();((NULL) null).printnull();Null
- 你寫的 return null 正確嗎?Null
- 使用Java 8 Optional避免空指標異常Java指標
- 你設計的遊戲好不好玩?遊戲
- 不是智慧不好用 而是你不會用
- NULL列時,如何使得IS NULL或者IS NOT NULL可以使用索引來提高查詢效率Null索引
- 別名的使用和nullNull
- /dev/null和標準*使用devNull
- 如何正確使用Java8的Optional機制Java
- Java 8 之 OptionalJava
- 為什麼你學不好Web前端?這些原因你需瞭解Web前端
- 工作做不完,假期不好過?ChatGPT 幫你高效工作ChatGPT
- 為什麼你用不好Numpy的random函式?random函式
- dart系列之:和null說再見,null使用最佳實踐DartNull
- 為什麼IDEA不推薦你使用@Autowired ?Idea
- IS NULL和IS NOT NULLNull
- 聊一聊Java8 Optional,讓你的程式碼更加優雅Java
- 煩人的Null,你可以走開點了Null
- 你真的理解T-sql中的NULL嗎?SQLNull
- 你為什麼學不好Python?論學習方法Python