如何避免Java程式碼中的空指標錯誤NullPointerException? - foojay

banq發表於2020-12-24

根據2016年的一項研究,可怕的程式碼 (簡稱NPE)是生產中最常見的Java異常 。在本文中,我們將探討與之抗衡的主要技術:自我驗證模型和 Optional 包裝器。
 

自我驗證模型
想象一下業務規則:每個客戶都必須設定生日。有多種方法可以實現此約束:在建立和更新用例上驗證使用者資料,透過NOT NULL 資料庫約束強制執行 和/或在Customer實體的建構函式中實現空檢查許可權
以下是當今最常用的用於對建構函式進行空值檢查的形式:

public Customer(@NonNull Date birthDate) { // 3
  assert (birthDate != null); // 4
  if (birthDate == null) { // 1
     throw new IllegalArgumentException();
  }
  this.birthDate = Objects.requireNonNull(birthDate); // 2
}

上面的程式碼包含4種 替代 方法來完成相同的事情,任何一個當然就足夠了:
  1. 經典 if 判斷
  2. 使用Java 8的檢查 java.util.Objects -在當今開發中的專案中使用最廣泛
  3. Lombok @NonNull 導致將 if 檢查新增到生成的位元組碼中。
  4. assert關鍵字:我個人不喜歡它,因為可以透過JVM引數全域性禁用斷言

如果存在出生日期的設定器,則檢查將移至該位置,而建構函式將呼叫該設定器。 
在資料物件的建構函式中強制執行空檢查具有明顯的優勢:沒有人會忘記這樣做。但是,透過反射直接寫入例項欄位的框架可能會繞過此檢查。Hibernate預設情況下會執行此操作,因此我的建議是也將資料庫中相應的必需列標記為NOT NULL 確保資料返回一致。不幸的是,在必須容忍“不正確的歷史資料”的傳統系統中,問題可能變得更加複雜。

技巧:Hibernate在每個永續性實體上都需要一個無引數的建構函式,但是可以將該建構函式標記為 protected 對開發人員隱藏。 
如果 null 確實是一個有效值呢?例如,假設我們的客戶可能沒有會員卡,因為她尚未建立會員卡,或者可能不想註冊會員卡。我們將在下一節討論這種情況。
 

Getter返回Optional
最佳實踐:由於Java 8中,每當一個函式需要返回 null,應該返回 Optional。
假設我們正在談論對映到關聯式資料庫的Entity,那麼如果您沒有NOT NULL 在相應的列上強制執行 ,則該欄位的getter應該返回 Optional。對於不提供null 保護的非持久資料物件或NoSQL資料儲存 ,上一節提供了有關如何在實體程式碼中以程式設計方式強制執行空檢查的想法。 
為了簡化過渡到這種新方式的過程,可以使用以下安全步驟序列:
首先,建立第二個getter返回 Optional:

public Optional getMemberCardOpt() { 
   return Optional.ofNullable(memberCard); 
}

其次,更改原始的getter以委託給新的getter:

public String getMemberCard() { 
    return getMemberCardOpt().orElse(null); 
}

 

相關文章