Java中使用Optional檢測並獲得非空值的幾種方法

banq發表於2024-05-20

Optional是 Java 8 中作為java.util包的一部分引入的類。它充當可能包含也可能不包含非空值的容器。 Optional可以幫助我們更有效地處理null值並避免程式碼中出現NullPointerException 。

什麼是Optional類?
在 Java 中,引用可能指向也可能不指向記憶體中的物件。換句話說,引用可以為null。因此,這可能會引發NullPointerException異常。
為了解決這個問題,Java 8 中引入了Optional類。

將引用包裝在Optional中可以讓我們更好地表達值存在或不存在的可能性。

此外,我們可以利用Optional類上的各種實用方法(例如isPresent()) 來避免遇到NullPointerException。


本文介紹:

  1. 使用Optional時的一項常見任務是檢查它是否包含等於特定物件的值?
  2. 使用靜態工廠方法Optional.of()和Optional.ofNullable()方法來獲取給定引用的Optional 


1、如何檢查Optional是否包含等於T物件的值

首先,我們先明確一下等於檢查的要求。

假設我們有兩個物件;一個是型別T的非 null valueOfT物件,另一個是型別Optional<T>的opt例項。

  • 如果opt為空,則它不包含任何 value。也就是說,它裡面的值不能等於任何目標物件,因此檢查將始終返回false。
  • 相反,如果opt是“present”,我們需要驗證opt攜帶的值是否等於valueOfT。

因此,簡單地說,我們將執行“ opt present and equals valueOfT ”檢查。

在本教程中,我們將探索完成該任務的各種方法。為了簡單起見,我們將使用String作為T的示例來演示每種方法。讓我們建立兩個字串常量:

static final String A_B_C = <font>"a b c";
static final String X_Y_Z =
"x y z";

我們將在單元測試中使用這些字串值來展示不同方法的結果。

接下來,讓我們深入研究程式碼並研究如何實現檢查。

equals()方法
Optional類重寫了equals()方法。如果存在兩個可選物件並且它們儲存的值相等,則該方法返回true。因此,我們可以首先從valueOfT建立一個Optional例項,然後使用Optional.equals() 與給定的opt物件執行檢查:

opt.isPresent() && opt.equals(Optional.of(valueOfT));

接下來,我們來檢查一下這種方法是否能按預期工作。

首先,我們看一下opt不存在的情況:

Optional<String> opt = Optional.empty();
assertFalse(opt.isPresent() && opt.equals(Optional.of(A_B_C)));

正如我們提到的,如果opt為空,則無論valueOfT 的值是什麼,整個檢查都應該返回false。

接下來,讓我們看看當opt存在時,這種方法是否會產生預期的結果:

opt = Optional.of(X_Y_Z);
assertFalse(opt.isPresent() && opt.equals(Optional.of(A_B_C)));
 
opt = Optional.of(A_B_C);
assertTrue(opt.isPresent() && opt.equals(Optional.of(A_B_C)));

正如我們所看到的,這種方法解決了問題,但它建立了一箇中間的Optional物件。

Optional.get()
檢查opt中包含的值是否等於valueOfT 的直接方法是首先使用Optional.get()檢索opt中的值,然後驗證其與valueOfT是否相等:

opt.isPresent() && opt.get().equals(valueOfT);

接下來,讓我們遵循此模式並使用相同的輸入來驗證它是否產生正確的結果:

Optional<String> opt = Optional.empty();
assertFalse(opt.isPresent() && opt.get().equals(A_B_C));
 
opt = Optional.of(X_Y_Z);
assertFalse(opt.isPresent() && opt.get().equals(A_B_C));
 
opt = Optional.of(A_B_C);
assertTrue(opt.isPresent() && opt.get().equals(A_B_C));

正如程式碼所示,該解決方案不會建立額外的物件。

使用Optional.map()和Optional.orElse()
我們期望在“ opt present and equals valueOfT ”檢查後獲得一個布林值。因此,我們可以將這個問題視為透過遵循一定的規則將Optional物件轉換為布林值。

Optional類提供map() 來將當前的Optional轉換為另一個攜帶不同值的Optional。

此外,orElse()方法返回由Optional例項包裝的值(如果存在)。否則,如果Optional為空,orElse()返回我們指定的值。

因此,我們可以結合這兩種方法來執行相等性檢查並從給定的Optional中獲取一個布林值:

opt.map(v -> v.equals(valueOfT)).orElse(false);

接下來,讓我們看看它是否適用於我們的輸入:

Optional<String> opt = Optional.empty();
assertFalse(opt.map(A_B_C::equals).orElse(false));
 
opt = Optional.of(X_Y_Z);
assertFalse(opt.map(A_B_C::equals).orElse(false));
 
opt = Optional.of(A_B_C);
assertTrue(opt.map(A_B_C::equals).orElse(false));

此解決方案還會產生一個由可選物件map() 建立的中間物件。然而,這是一種實用且流暢的方法。

2、使用靜態工廠方法Optional.of()和Optional.ofNullable()方法來獲取給定引用的Optional 
瞭解了靜態工廠方法Optional.of()和Optional.ofNullable()之間的主要區別以及何時最適合使用它們。

Optional.of ()方法
當我們確定有一個非空引用時,我們應該使用Optional.of()靜態工廠方法。

假設我們有一個本地String 變數,我們想從中獲取一個Optional:

@Test
void givenNonNullReference_whenUsingOptionalOf_thenObtainOptional() {
    String s = <font>"no null here";
    assertThat(Optional.of(s))
      .isNotEmpty()
      .hasValue(
"no null here");
}

從我們的斷言中,我們可以看到我們的可選值不為空。換句話說,實用程式方法isPresent()(返回Optional是否有值)將返回true。 

但是,當我們對空引用使用此方法時,我們將遇到NullPointerException。

@Test
void givenNullReference_whenUsingOptionalOf_thenNullPointerExceptionThrown() {
    String s = null;
    assertThatThrownBy(() -> Optional.of(s))
      .isInstanceOf(NullPointerException.class);
}

Optional.ofNullable ()方法
當我們有一個可能為空或不為空的引用時,我們應該使用Optional.ofNullable()靜態工廠方法。因此,對於null的引用,我們不會遇到NullPointerException。相反,我們將獲得一個空的Optional:

@Test
void givenNullReference_whenUsingOptionalOfNullable_thenObtainOptional() {
    String s = null;
    assertThat(Optional.ofNullable(s)).isEmpty();
}

公平地說,為什麼我們不應該總是使用Optional.ofNullable()而不是Optional.of()。

使用Optional.of()允許我們透過丟擲異常來立即停止程式碼的執行。正如我們之前在獲取null引用的Optional時發現的那樣,就會發生這種情況。換句話說,使用Optional.of()方法可以讓我們遵守早期失敗的原則。

另外,我們可能會遇到開發人員使用這些靜態工廠方法作為使用函數語言程式設計的入口點。這是透過使用Optional類上的方法來實現的,這些方法使用函式物件作為方法引數,例如map()。

 

相關文章