整理 Effective Java 書中 Item 17: Minimize mutability 心得筆記

主旨

當你設計類別時,預設應該先問自己:這個物件真的需要改變狀態嗎?如果不需要,請讓它不可變(Immutable)。不可變類別更安全、更好測試、也更容易被重用與快取。

如何設計不可變類別?

只要遵守這五個原則就能做到:

  1. 不提供修改狀態的方法(setter)
  2. 類別設為 final(禁止被繼承)
  3. 所有欄位設為 private final
  4. 不讓外部取得內部可變物件(防守性複製)
  5. 建構後物件狀態固定

✅ 範例:一個不可變的 User

public final class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
}

這個 User 類別一旦建立,就無法修改 name 與 age,也沒有任何 setName()setAge() 方法,這就是不可變類別的基本形式。


為什麼要這麼做?

  • 簡單安全:狀態不會變,就不會出錯
  • 天然支援多執行緒:不需要同步鎖
  • 適合當 Map 的 key、Set 的元素
  • 更容易被快取或共用:例如 String 就是不可變的

✅ 好處延伸:可提供常用值常數

public static final User EMPTY = new User("", 0);

例外狀況:真的很需要變的情況

如果你要表示一個計數器的狀態或畫面中物件的位置,可能就需要可變類別。但即使如此,也應盡量限制可變範圍

public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getValue() {
        return count;
    }
}

這裡的 Counter 是可變的,但它的可變行為是被控制住的,只有 increment() 可以改變狀態,這也是最小化可變性的一種方式。


避免的狀況:過度暴露狀態

以下寫法就違反了不可變原則,讓外部可以直接修改欄位:

public class BadUser {
    public String name;
    public int age;
}

只要任何人能自由改動物件狀態,這個類別就變得難以維護、容易出錯,也難以保證資料一致性。


小結

  • ✅ 如果不需要修改狀態,就做成不可變
  • ✅ 所有欄位設為 private final
  • ✅ 沒有 setter,只保留 getter
  • ✅ 必要時可提供 companion 類別(如 StringBuilder 對應 String

不可變類別是好習慣的起點,也能減少維護成本與意外行為,是穩健程式設計的基石。