Immutable objects là những đối tượng có trạng thái không thể thay đổi sau khi khởi tạo. Đôi khi cần tạo immutable class theo yêu cầu. Ví dụ, tất cả primitive wrapper classes (Integer, Byte, Long, Float, Double, Character, Boolean và Short) là immutable trong Java. String class cũng là một immutable class.
Để tạo custom immutable class, chúng ta phải làm các bước sau
- Khai báo class là final để không thể extend.
- Làm tất cả fields private để không cho truy cập trực tiếp.
- Không cung cấp setter methods (phương thức sửa đổi fields) cho biến, để không thể set.
- Làm tất cả mutable fields final để giá trị chỉ được gán một lần.
- Khởi tạo tất cả fields qua constructor làm deep copy.
- Thực hiện cloning đối tượng trong getter methods để trả về bản sao thay vì trả về tham chiếu đối tượng thực tế.
- Nếu instance fields bao gồm tham chiếu đến mutable objects, không cho phép những đối tượng đó bị thay đổi.
- Không cung cấp phương thức sửa đổi mutable objects.
- Không chia sẻ tham chiếu đến mutable objects. Không bao giờ lưu tham chiếu đến external, mutable objects được truyền cho constructor. Nếu cần, tạo bản sao và lưu tham chiếu đến bản sao. Tương tự, tạo bản sao của internal mutable objects khi cần để tránh trả về bản gốc trong các phương thức của chúng ta.
Ví dụ
// Employee.java
final class Employee {
private final String empName;
private final int age;
private final Address address;
public Employee(String name, int age, Address address) {
super();
this.empName = name;
this.age = age;
this.address = address;
}
public String getEmpName() {
return empName;
}
public int getAge() {
return age;
}
/* public Address getAddress() {
return address;
} */
public Address getAddress() throws CloneNotSupportedException {
return (Address) address.clone();
}
}
// Address.java
class Address implements Cloneable {
public String addressType;
public String address;
public String city;
public Address(String addressType, String address, String city) {
super();
this.addressType = addressType;
this.address = address;
this.city = city;
}
public String getAddressType() {
return addressType;
}
public void setAddressType(String addressType) {
this.addressType = addressType;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Address Type - "+addressType+", address - "+address+", city - "+city;
}
}
// MainClass.java
public class MainClass {
public static void main(String[] args) throws Exception {
Employee emp = new Employee("Adithya", 34, new Address("Home", "Madhapur", "Hyderabad"));
Address address = emp.getAddress();
System.out.println(address);
address.setAddress("Hi-tech City");
address.setAddressType("Office");
address.setCity("Hyderabad");
System.out.println(emp.getAddress());
}
}Trong ví dụ trên, thay vì trả về đối tượng Address gốc, chúng ta trả về bản sao deep cloned của instance đó. Class address phải implement interface Cloneable.
Kết quả
Address Type - Home, address - Madhapur, city - Hyderabad Address Type - Home, address - Madhapur, city - Hyderabad