Vavr Option:Java Optional 的另一個選項
每當涉及Java,總會有很多選項。 這篇文章討論了 Java 基礎類 Optional 用法,與 Vavr 中的對應方法進行比較。Java 8最早引入了 Optional,把它定義為“一種容器物件,可以儲存 null 或非 null 值”。
通常,在返回值可能為null的地方,會出現NullPointerException。開發人員可以使用 Optional 避免 null 值檢查。在這種情況下,Optional 提供了一些方便的功能。但可惜的是,Java 8並沒有包含所有功能。Optional中的某些功能需要使用 Java 11。要解決這類問題還可以使用 Vavr Option類。
本文將介紹如何使用 Java Optional類,並與 Vavr Option 進行比較。注意:示例程式碼要求使用Java 11及更高版本。所有程式碼在 Vavr0.10.2環境下完成測試。
讓我們開始吧。
Java Optional 簡介
Optional 並不是什麼新概念,像 Haskell、Scala 這樣的函數語言程式設計語言已經提供了實現。呼叫方法後,返回值未知或者不存在(比如 null)的情況下,用 Optional 處理非常好用。下面透過例項進行介紹。
新建 Optional 例項
首先,需要獲得 Optional 例項,有以下幾種方法可以新建 Optional 例項。不僅如此,還可以建立empty Optional。方法一,透過 value 建立,過程非常簡單:
Optional<Integer> four = Optional.of(Integer.valueOf(
4));
if (four.isPresent){
System.out.println(
"Hoorayy! We have a value");
}
else {
System.out.println(
"No value");
}
為Integer 4 新建一個Optional例項。這種方法得到的 Optional 始終包含一個 value 且不為 null,例如上面這個示例。使用 ifPresent() 可以檢查value是否存在。可以注意到 four 不是 Integer,而是一個裝有整數的容器。如果確認 value 存在,可以用 get() 方法執行拆箱操作。具有諷刺意味的是,呼叫 get() 前如果不進行檢查,可能會丟擲 NoSuchElementException。
方法二,得到 Optional 物件的另一種方法是使用 stream。Stream提供的一些方法會返回Optional,可以用來檢查結果是否存在,例如:
-
findAny
-
findFirst
-
max
-
min
-
reduce
檢視下面的程式碼段:
Optional<Car> car = cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();
方法三,使用 Nullable 新建 Optional。可能產生 null:
Optional<Integer> nullable = Optional.ofNullable(client.getRequestData());
最後,可以新建一個 empty Optional:
Optional<Integer> nothing = Optional.empty();
如何使用 Optional
獲得 Optional 物件後即可使用。一種典型的場景是在 Spring 倉庫中根據 Id 查詢記錄。可以使用 Optional 實現程式碼邏輯,避免 null 檢查(順便提一下,Spring 也支援 Vavr Option)。比如,從圖書倉庫裡查詢一本書。
Optional<Book> book = repository.findOne("some id");
首先,如果有這本書,可以繼續執行對應的業務邏輯。在前面的章節用 if-else實現了功能。當然,還有其他辦法:Optional 提供了一個方法,接收 Consumer 物件作為輸入:
repository.findOne("some id").ifPresent(book -> System.out.println(book));
還可以直接使用方法引用,看起來更簡單:
repository.findOne("some id").ifPresent(System.out::println);
如果倉庫中沒有該書,可以用ifPresentOrElseGet提供回撥函式:
repository.findOne(
"some id").ifPresentOrElseGet(book->{
// 如果 value 存在
}, ()->{
// 如果 value 不存在
});
如果結果不存在,可以返回另一個value:
Book result = repository.findOne("some id").orElse(defaultBook);
但是,Optional 也有缺點,使用時需要注意。最後一個例子中,“確保”無論如何都能獲得一本書,可能在倉庫中,也可能來自 orElse。但如果預設的返回值不是常量或者需要支援一些複雜方法該怎麼辦?首先,Java 無論如何都會執行 findOne,然後呼叫 orElse方法。預設返回值可以為常量,但正如我之前所說那樣,執行過程比較耗時。
另一個示例
下面用一個簡單的示例介紹如何實際使用 Optional 和 Option 類。有一個 CarRepository,可以根據提供的 ID(比如車牌號)查詢汽車,接下來用這個示例介紹如何使用 Optional 和 Option。
首先,加入下面程式碼
從 POJO 類 Car 開始。它遵循 immutable 模式,所有欄位都標記為 final,只包含 getter 沒有 setter。初始化時提供所有資料:
public
class Car {
private final String name;
private final String id;
private final String color;
public
Car
(String name, String id, String color){
this.name = name;
this.id = id;
this.color = color;
}
public String
getId
(){
return id;
}
public String
getColor
() {
return color;
}
public String
getName
() {
return name;
}
@
Override
public String
toString
() {
return
"Car "+name+
" with license id "+id+
" and of color "+color;
}
}
接下來建立 CarRepository類。提供兩種方法根據Id查詢汽車:一種是老辦法,使用 Optional。和之前在 Spring 倉庫的做法類似,結果可能為 null。
public
class
CarRepository {
private
List<Car> cars;
public
CarRepository
()
{
getSomeCars();
}
Car
findCarById
(String id)
{
for
(Car car: cars){
function(){ //外匯跟單
if
(car.getId().equalsIgnoreCase(id)){
return
car;
}
}
return
null;
}
Optional<Car> findCarByIdWithOptional(String id){
return
cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();
}
private
void
getSomeCars
()
{
cars =
new
ArrayList<>();
cars.add(
new
Car(
"tesla"
,
"1A9 4321"
,
"red"
));
cars.add(
new
Car(
"volkswagen"
,
"2B1 1292"
,
"blue"
));
cars.add(
new
Car(
"skoda"
,
"5C9 9984"
,
"green"
));
cars.add(
new
Car(
"audi"
,
"8E4 4321"
,
"silver"
));
cars.add(
new
Car(
"mercedes"
,
"3B4 5555"
,
"black"
));
cars.add(
new
Car(
"seat"
,
"6U5 3123"
,
"white"
));
}
}
注意:初始化過程會在倉庫中新增一些汽車模擬資料,便於演示。為了突出重點,避免問題複雜化,下面的討論專注於 Optional 和 Option。
使用Java Optional
使用JUnit建立一個新測試:
@
Test
void
getCarById
(){
Car car = repository.findCarById(
"1A9 4321");
Assertions.assertNotNull(car);
Car nullCar = repository.findCarById(
"M 432 KT");
Assertions.assertThrows(NullPointerException.
class, ()->{
if (nullCar == null){
throw
new NullPointerException();
}
});
}
上面的程式碼段採用了之前的老辦法。查詢捷克牌照 1A9 4321對應的汽車,檢查該車是否存在。輸入俄羅斯車牌找不到對應的汽車,因為倉庫中只有捷克車。結果為 null 可能會丟擲 NullPointerException。
接下來用Java Optional。第一步,獲得 Optional 例項,從儲存庫中使用指定方法返回 Optional:
@
Test
void
getCarByIdWithOptional
(){
Optional<Car> tesla = repository.findCarByIdWithOptional(
"1A9 4321");
tesla.ifPresent(System.out::println);
}
這時呼叫findCarByIdWithOptional方法列印車輛資訊(如果有的話)。執行程式,得到以下結果:
Car tesla with license id 1A9 4321 and of color red
但是,如果程式碼中沒有特定方法該怎麼辦?這種情況可以從方法返回可能包含 null 的 Optional,稱為nullable。
Optional<Car> nothing = Optional.ofNullable(repository.findCarById(
"5T1 0965"));
Assertions.assertThrows(NoSuchElementException.
class, ()->{
Car car = nothing.orElseThrow(()->
new NoSuchElementException());
});
上面這段程式碼段中,我們發現了另一種方法。透過 findCarById 建立 Optional,如果未找到汽車可以返回 null。沒有找到車牌號 5T1 0965汽車時,可以用 orElseThrow 手動丟擲 NoSuchElementException。另一種情況,如果請求的資料不在倉庫中,可以用orElse返回預設值:
Car audi = repository.findCarByIdWithOptional(
"8E4 4311")
.orElse(
new Car(
"audi",
"1W3 4212",
"yellow"));
if (audi.getColor().equalsIgnoreCase(
"silver")){
System.out.println(
"We have silver audi in garage!");
}
else {
System.out.println(
"Sorry, there is no silver audi, but we called you a taxi");
}
好的,車庫裡沒有找到銀色奧迪,只好叫車了!
使用 Vavr Option
Vavr OptionOption提供了另一種解決辦法。首先,在專案中新增依賴,(使用 Maven)安裝 Vavr:
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>
0.10.2</version>
</dependency>
簡而言之,Vavr 提供了類似的 API 新建 Option 例項。可以從 nullable 新建 Option 例項,像下面這樣:
Option<Car> nothing = Option.of(repository.findCarById("T 543 KK"));
也可以用 none 靜態方法建立一個empty容器:
Option<Car> nullable = Option.none();
此外,還有一種方法可以用 Java Optional 新建 Option。看下面這段程式碼:
Option<Car> result = Option.ofOptional(repository.findCarByIdWithOptional("5C9 9984"));
使用 Vavr Option,可以使用與 Optional相同的 API 來完成上述任務。例如,設定預設值:
Option<Car> result = Option.ofOptional(repository.findCarByIdWithOptional(
"5C9 9984"));
Car skoda = result.getOrElse(
new Car(
"skoda",
"5E2 4232",
"pink"));
System.out.println(skoda);
或者,請求的資料不存在時可以丟擲異常:
Option<Car> nullable = Option.none();
Assertions.assertThrows(NoSuchElementException.
class, ()->{
nullable.getOrElseThrow(()->
new NoSuchElementException());
});
另外,當資料不可用時,可以執行以下操作:
nullable.onEmpty(()->{
///runnable
});
如何根據資料是否存在來執行相應操作,類似 Optional 中 ifPresent?有幾種實現方式。與 Optional 中 isPresent 類似,在 Option 中對應的方法稱為 isDefined:
if (result.isDefined()){
// 實現功能
}
然而,使用 Option能擺脫 if-else。 是否可以用Optional相同的方式完成? 使用 peek 操作:
result.peek(val -> System.out.println(val)).onEmpty(() -> System.out.println("Result is missed"));
此外,Vavr Option還提供了一些其他非常有用的方法,在函數語言程式設計上比Optional類效果更好。 因此,建議您花一些時間來探索 Vavr Option javadocs嘗試使用這些API。 我會持續跟進一些類似 map、narrow、isLazy 和 when 這樣有趣的功能。
另外,Option只是 Vavr 開發庫的一部分,其中還包含了許多其他關聯類。 不考慮這些類直接與 Optional 比較是不對的。 接下來我會繼續編寫 Vavr 主題的系列文章,介紹 Vavr 相關技術例如 Try、Collections 和 Streams。 敬請關注!
總結
本文中,我們討論了 Java Optional 類。 Optional 並不是什麼新概念,像 Haskell、Scala這樣的函數語言程式設計語言已經提供了實現。 呼叫方法後,返回值未知或者不存在(比如 null)的情況下,Optional 非常有用。 然後,介紹了 Optional API,並設計了一個汽車搜尋示例進行說明。 最後,介紹了 Optional 的另一種替代方案 Vavr Option 並透過示例進行了介紹。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2662859/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 實用的可選項(Optional)擴充套件套件
- js如何互換兩個select下拉選單的option項JS
- 如何清空select下拉選單的所有option項
- javascript刪除或者新增option選項例項程式碼JavaScript
- select下拉選擇第一個選項為空白、option無法選中的解決辦法,
- js獲取select下拉選單的所有option項JS
- 選中select下拉選單option項實現提交效果
- 通過ajax方式動態新增select下拉選單的option選項
- jquery獲取select下拉選中option項的value值jQuery
- 如何在select下拉選單的指定索引位置插入option項索引
- js獲取select下拉選單中option項的數目JS
- 刪除和新增select下拉選單option項程式碼例項
- CSS :optional 選擇器CSS
- jquery select下拉選單新增或者刪除option項jQuery
- jQuery刪除select下拉選單中所有option項jQuery
- 兩個select下拉選單的option相互移動
- Java 8 之 OptionalJava
- javascript新增和刪除select下拉選單option項程式碼例項JavaScript
- Java Optional的orElse()與orElseGet()兩個方法比較 - BaeldungJava
- 如何設定select下拉選單option項顯示的數目
- javascript如何獲取當前選中的option項的文字和value值JavaScript
- js如何動態為select下拉選單新增option項JS
- Java Optional使用的最佳實踐Java
- Java Optional使用指南Java
- Java8 Optional類Java
- Java基礎系列-OptionalJava
- java8-Optional APIJavaAPI
- java程式中編譯另一個java程式Java編譯
- 講講Java8的Optional類Java
- 關於一個java專案呼叫另一個java專案的心得Java
- RAC環境一個例項何時會歸檔另一個例項的日誌
- Java 8 新特性---Optional類Java
- Nginx的另一個選擇 - Traefik 入手及簡單配置Nginx
- jq動態修改select 的option值,使option值自動選中
- 使用Java Optional類的最佳實踐 - oracleJavaOracle
- java 8 Stream,Optional的流庫詳解Java
- 使用 Java 8 Optional 的正確姿勢Java
- AngularJS下拉選單select在option動態變化之後多出了一個錯誤項的問題AngularJS