Java Optional vs Vavr Option 不同之處曉得?


討論一個重要的Java主題-Optional類的用法-並將其與Vavr庫中的替代方法進行比較。可選最初在Java 8中引入,並定義為“可能包含也可能不包含非null值的容器對象”。開發人員利用Optionals來避免在代NullPointerException。在這種情況下,Optional為我們提供了一些精美的功能,但並非所有功能都在第8版中引入,某些功能需要Java11。另一種解決這些問題的方法是使用Vavr的Option類。在本文中,我們將學習如何使用Java的Optional類,然後將其與Vavr的Option類進行比較。注意:此代碼需要Java 11 +


Java Optional vs Vavr Option 不同之處曉得?


Java Optional 簡介

已經以功能性編程語言(如Haskell或Scala)實現了。在對方法調用可能返回未知值或不存在的值(例如null)的情況進行建模時,它被證明非常有用。

創建 Optional

首先,我們需要創建Optional的實例。有幾種方法可以實現。我們還可以創建一個空的Optional。看看第一種方法,這非常簡單:

<code>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>/<code>

我們從4的Integer值構建一個Optional,這意味著始終應該有一個值並且它不能為null,但這只是一個例子。我們使用ifPresent()方法檢查值是否存在。您可以注意到四個不是整數;它是一個容器,裡面裝有整數。當我們確定該值在內部時,可以使用get()獲取值。如果我們不進行檢查就使用get(),則可能NoSuchElementException結尾。獲得可選參數的另一種方法是使用流。幾種終端流的方法返回Optional,因此我們可以對其進行操作並檢查其存在與否,例如:

  • findAny
  • findFirst
  • max
  • min
  • reduce

代碼片段

<code>Optional car = cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();/<code>

創建 Nullable:

<code>Optional<integer> nullable = Optional.ofNullable(client.getRequestData());/<integer>/<code>

創建 空 Optional:

<code>Optional<integer> nothing = Optional.empty();/<integer>/<code>

使用 Optional

一種最普遍的情況是在Spring存儲庫中使用它來通過ID查找一條記錄,因此我們可以在Optional上構建邏輯並避免空檢查(順便說一句,Spring還支持Vavr Options)。假設我們有一個圖書庫,想找到一本書。

<code>Optional<book> book = repository.findOne("some id");/<book>/<code>


Java Optional vs Vavr Option 不同之處曉得?

首先,如果有這本書,我們可以執行一些邏輯。我們在上一節中使用if-else做到了這一點,但是我們不需要:可選為我們提供了一種接受對象的消費者的方法:

<code>repository.findOne("some id").ifPresent(book -> System.out.println(book));/<code>

或者,我們可以使其更簡單。我們可以使用方法引用:

<code>repository.findOne("some id").ifPresent(System.out::println);/<code>

如果存儲庫中沒有書籍,則可以使用theifPresentOrElseGet方法提供替代回調

<code>repository.findOne("some id").ifPresentOrElseGet(book->{
// if value is presented
}, ()->{
// if value is absent
});/<code>

另外,如果未顯示運算結果,我們可以設置默認值:

<code>Book result = repository.findOne("some id").orElse(defaultBook);/<code>

但是,使用Optionals,我們需要記住可能的缺點。在最後一個示例中,我們“保證”自己無論如何都能獲得一本書。它顯示存儲或來自orElse。但是,如果此默認值不是恆定的,但又需要一些複雜的方法怎麼辦?首先,Java無論如何都會評估findOne。然後,它必須處理orElse方法。是的,如果它只是一個默認常數,就可以了。

另一個實例

讓我們創建一個簡單的示例來檢查如何實際使用Optional和Option類。我們將有一個CarRepository,可以根據提供的ID(例如在車牌號上)找到一輛汽車。然後,我們將看到如何操作Optional和Options。

首先,添加基礎代碼

它遵循不可變模式,因此所有字段都是final,我們只有getter而沒有setter。初始化期間將提供所有數據。

<code>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;
}
}/<code>

The second thing is to create the CarRepository class. It requires two options for finding car by Id — using the old way with a possible null result and using Optional, as we do in Spring repositories.

第二,是創建CarRepository類。findCarById 可能返回null.

<code>public class CarRepository {

private List cars;

public CarRepository(){
getSomeCars();
}

Car findCarById(String id){
for (Car car: cars){
if (car.getId().equalsIgnoreCase(id)){
return car;
}
}
return null;
}

Optional 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"));
}
}
/<code>


Java Optional 查詢Car

測試代碼片段

<code>@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();
}
});
}/<code>

上面的代碼段演示了舊方法。我們找到了一輛帶有車牌1A9 4321的汽車,並檢查了該汽車的存在。

<code>@Test
void getCarByIdWithOptional(){
Optional tesla = repository.findCarByIdWithOptional("1A9 4321");
tesla.ifPresent(System.out::println);
}
/<code>

在這種情況下,我們使用findCarByIdWithOptional方法,然後打印Car(如果有的話)。如果運行它,將得到以下輸出:

<code>Car tesla with license id 1A9 4321 and of color red/<code>


但是,如果我們的代碼中沒有該特殊方法,該怎麼辦?在這種情況下,我們可以從可能返回空值的方法中獲取Optional。它被稱為可空的。

<code>Optional nothing = Optional.ofNullable(repository.findCarById("5T1 0965"));
Assertions.assertThrows(NoSuchElementException.class, ()->{
Car car = nothing.orElseThrow(()->new NoSuchElementException());
});
/<code>


在此代碼段中,我們找到了另一種方法。我們從findCarById創建Optional,如果找不到車,它可以返回null。當存在帶有牌照5T1 0965的所需汽車時,我們手動使用orElseThrow方法引發NoSuchElementException。另一種情況是,如果請求的數據在存儲庫中不可用,則將orElse與默認值一起使用:

<code>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");
}/<code>


Vavr Option

Vavr Option是處理這些任務的另一種方法。首先,使用依賴項管理在您的項目中安裝Vavr(我使用Maven):

<code><dependency>
<groupid>io.vavr/<groupid>
<artifactid>vavr/<artifactid>
<version>0.10.2/<version>
/<dependency>/<code>

簡而言之,Vavr具有類似的API可創建Option實例。我們可以從nullable創建Option,如下所示:

<code>Option nothing = Option.of(repository.findCarById("T 543 KK"));/<code>

或者我們可以使用一個靜態方法創建一個空容器:

<code>Option nullable = Option.none();/<code>

此外,還有一種方法可以從Java創建Option(可選)!看一下下面的代碼片段:

<code>Option result = Option.ofOptional(repository.findCarByIdWithOptional("5C9 9984"));/<code>


藉助Vavr選項,我們可以使用與Optional相同的API來完成上述任務。例如,我們可以通過類似的方式設置默認值:

<code>Option result = Option.ofOptional(repository.findCarByIdWithOptional("5C9 9984"));
Car skoda = result.getOrElse(new Car("skoda", "5E2 4232", "pink"));
System.out.println(skoda);
/<code>

拋出異常

<code>Option nullable = Option.none();
Assertions.assertThrows(NoSuchElementException.class, ()->{
nullable.getOrElseThrow(()->new NoSuchElementException());
});
/<code>

或者,當數據不可用時,我們可以執行以下操作

<code>nullable.onEmpty(()->{
///runnable
});/<code>

如果我們需要根據數據的存在來執行操作,就像我們使用Optional的ifPresent一樣,該怎麼辦?我們可以通過幾種方式做到這一點。在Option中有一個相等的方法isPresent,在Option中被稱為isDefined:

<code>if (result.isDefined()){
// do something
}/<code>


但是,我們使用Option來擺脫if-else構造。我們可以以與Optional相同的方式浮動嗎?我們可以通過窺視的存在來執行操作:

<code>result.peek(val -> System.out.println(val)).onEmpty(() -> System.out.println("Result is missed"));/<code>


此外,Vavr Option中還有其他一些非常有用的方法,這些方法可以使您的代碼比內置的Optional類具有更多功能。因此,我鼓勵您花一些時間來探索Vavr Option Javadocs並嘗試使用這些API。

總結


Java Optional vs Vavr Option 不同之處曉得?


在本文中,我們討論了Java中的Optional類。可選的概念並不是什麼新鮮事物,已經在其他功能編程語言(例如Haskell和Scala)中實現了。在對方法調用可能返回未知值或不存在的值(例如null)的情況進行建模時,它被證明非常有用。然後,我們探索了它的API,並創建了一些示例,這些示例使用可選邏輯查找汽車並處理結果。最後,我們找到了Optional-Vavr的Option的替代方法並描述了其方法。

希望你喜歡!在評論中留下想法或問題。


分享到:


相關文章: