Immutable objects คือออบเจ็กต์ที่ state ไม่สามารถเปลี่ยนแปลงได้หลังจาก initialized แล้ว บางครั้งจำเป็นต้องทำ immutable class ตาม requirement ตัวอย่างเช่น ทุก primitive wrapper classes (Integer, Byte, Long, Float, Double, Character, Boolean และ Short) เป็น immutable ใน Java String class ก็เป็น immutable class เช่นกัน
ขั้นตอนการสร้าง custom immutable class
- ประกาศคลาสเป็น final เพื่อไม่ให้ extend ได้
- ทำให้ทุก fields เป็น private เพื่อไม่ให้เข้าถึงโดยตรง
- ไม่提供 setter methods (เมธอดที่แก้ไข fields) สำหรับตัวแปร เพื่อไม่ให้ตั้งค่าได้
- ทำให้ mutable fields ทั้งหมดเป็น final เพื่อให้กำหนดค่าได้ครั้งเดียว
- Initialize ทุก fields ผ่าน constructor โดยทำ deep copy
- ทำ cloning ของออบเจ็กต์ ใน getter methods เพื่อคืนค่า copy แทนที่จะคืน object reference จริง
- ถ้า instance fields มี references ไปยัง mutable objects ห้ามให้เปลี่ยนแปลงออบเจ็กต์เหล่านั้น
- ไม่提供 เมธอดที่แก้ไข mutable objects
- ไม่แชร์ references ไปยัง mutable objects ไม่ควรเก็บ references ไปยัง external mutable objects ที่ส่งผ่าน constructor หากจำเป็น ให้สร้าง copies และเก็บ references ไปยัง copies แทน เช่นเดียวกัน ให้สร้าง copies ของ internal mutable objects เมื่อจำเป็น เพื่อไม่ให้คืน originals ในเมธอดของเรา
ตัวอย่าง
// 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());
}
}
ในตัวอย่างข้างต้น แทนที่จะคืน Address object ต้นฉบับ เราจะคืน deep cloned copy ของ instance นั้น คลาส Address ต้อง implement interface Cloneable
ผลลัพธ์
Address Type - Home, address - Madhapur, city - Hyderabad Address Type - Home, address - Madhapur, city - Hyderabad