整理 Effective Java 書中 Item 54: Return empty collections instead of null心得筆記

主旨

當方法沒有資料可回傳時,有些人會選擇回傳 null,以代表「沒有資料」。但這麼做會讓呼叫端增加處理成本,也提高潛在錯誤發生的機率。正確做法應該是 回傳空的集合或陣列,這樣寫起來更簡潔、安全、直觀,也幾乎不會有效能損失。

點出問題:回傳 null 帶來的麻煩

先看個錯誤示範:

// ❌ 錯誤設計:回傳 null 表示沒有資料
public List<Cheese> getCheeses() {
    return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}

這樣會讓呼叫端寫出這樣的程式碼:

List<Cheese> cheeses = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON)) {
    System.out.println("Jolly good, just the thing.");
}

不只冗長,而且如果忘了判斷 null,就會發生 NullPointerException

❗ 缺點總整理:

  • 呼叫端 需要額外的 null 判斷
  • 忘了判斷就會出錯,錯誤可能潛藏很久
  • 實作端也需要處理更多額外邏輯
  • 跟 Java 的「物件導向」與「集合友善」哲學背道而馳

正確做法:回傳空集合或陣列

// ✅ 建議設計:直接回傳集合,不特別處理空資料
public List<Cheese> getCheeses() {
    return new ArrayList<>(cheesesInStock);
}

甚至你可以這樣寫來進一步優化效能(避免重複建立空集合):

// ✅ 進階:重複使用 immutable 的空集合
public List<Cheese> getCheeses() {
    return cheesesInStock.isEmpty()
        ? Collections.emptyList()
        : new ArrayList<>(cheesesInStock);
}

陣列也一樣

// ✅ 正確回傳陣列,不要 null
public Cheese[] getCheeses() {
    return cheesesInStock.toArray(new Cheese[0]);
}

或進一步優化:

// ✅ 進階:重複使用相同空陣列
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheeses() {
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

千萬不要這樣做:

// ⚠️ 誤用:預先配置陣列,效能反而更差
return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);

這種寫法反而會降低效能,應避免使用【Shipilëv 2016】。

實務建議

場景錯誤寫法正確寫法
沒資料時回傳 Listreturn null;return Collections.emptyList();
沒資料時回傳陣列return null;return new Type[0]; 或共享的空陣列
呼叫端if (list != null)可以直接用 for-each 迴圈或 .isEmpty()

小結

回傳 null 是懶惰的設計方式,不只增加錯誤風險,也讓程式變複雜。

請記住:

  • 回傳空集合或陣列,比回傳 null 更安全、可預測、好維護
  • 可以使用 Collections.emptyList()new T[0] 或共用空陣列
  • 除非測試證明效能瓶頸,否則不用過度優化

⚠️ 不要讓使用API的人去煩惱null判斷,直接回傳空集合或陣列讓他們根本不需要想!