Effective Java Item17 最小化可變性
整理 Effective Java 書中 Item 17: Minimize mutability 心得筆記
主旨
當你設計類別時,預設應該先問自己:這個物件真的需要改變狀態嗎?如果不需要,請讓它不可變(Immutable)。不可變類別更安全、更好測試、也更容易被重用與快取。
如何設計不可變類別?
只要遵守這五個原則就能做到:
- 不提供修改狀態的方法(setter)
- 類別設為
final(禁止被繼承) - 所有欄位設為
private final - 不讓外部取得內部可變物件(防守性複製)
- 建構後物件狀態固定
✅ 範例:一個不可變的 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)
不可變類別是好習慣的起點,也能減少維護成本與意外行為,是穩健程式設計的基石。
Read other posts