Trong Java, object được phân loại theo khả năng thay đổi trạng thái (state) sau khi tạo:
Định nghĩa
- Immutable Object: Trạng thái không thể thay đổi sau khi khởi tạo. Mọi trường (field) đều
final, không có setter, không cho phép subclass ghi đè (classfinal). - Mutable Object: Trạng thái có thể thay đổi qua setter, method sửa đổi, hoặc truy cập field trực tiếp.
Bảng so sánh
| Tiêu chí | Mutable Object | Immutable Object |
|---|---|---|
| Thay đổi state | Có (setter, method sửa đổi) | Không (chỉ getter, không setter) |
| Thread-safe | Không tự động (cần synchronized) | Tự nhiên thread-safe (không race condition) |
| Class final | Thường không final | Nên khai báo final class |
| Field | Không bắt buộc final | Tất cả private final |
| Defensive copy | Không cần | Cần cho field mutable (Date, List, array...) |
| Ví dụ JDK | StringBuilder, ArrayList, Date, StringBuffer | String, Integer, Long, LocalDate, UUID |
Ví dụ Immutable Class (Chuẩn)
public final class ImmutableStudent {
private final String name;
private final int age;
private final List subjects; // Mutable object → cần defensive copy
public ImmutableStudent(String name, int age, List subjects) {
this.name = name;
this.age = age;
// Defensive copy: không chia sẻ reference với caller
this.subjects = new ArrayList<>(subjects);
}
// Chỉ getter, KHÔNG setter
public String getName() { return name; }
public int getAge() { return age; }
// Trả về defensive copy để bảo vệ internal state
public List getSubjects() {
return new ArrayList<>(subjects); // Hoặc Collections.unmodifiableList(subjects)
}
// "With-er" method: trả về instance mới với thay đổi (functional style)
public ImmutableStudent withAge(int newAge) {
return new ImmutableStudent(this.name, newAge, this.subjects);
}
}
Ví dụ Mutable Class
public class MutableStudent {
private String name;
private int age;
private List subjects = new ArrayList<>();
public MutableStudent(String name, int age) {
this.name = name;
this.age = age;
}
// Getter & Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public List getSubjects() { return subjects; } // Trả về reference thật → caller có thể sửa!
public void addSubject(String s) { subjects.add(s); }
}
Tại sao Immutable quan trọng?
- Thread-safety: Dùng chung an toàn trong multi-thread không cần lock.
- Dễ reasoning: State không bao giờ thay đổi → code dễ hiểu, debug.
- Key của Map/Set: Chỉ immutable object an toàn làm key (hashCode/equals ổn định).
- Caching/Pooling: Có thể reuse an toàn (String pool, Integer cache).
- Failure atomicity: Nếu exception xảy ra trong constructor, object không bao giờ ở trạng thái invalid.
Lưu ý: Defensive Copy
Immutable class chứa field là object mutable (List, Date, Array, custom object) → phải copy khi nhận từ constructor và khi trả về getter. Nếu không, caller vẫn giữ reference và sửa đổi được internal state.