Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Java

ความแตกต่างระหว่าง Mutable และ Immutable Object ใน Java

Mutable Object = สถานะ (state) เปลี่ยนแปลงได้หลังสร้าง | Immutable Object = สถานะ **ไม่สามารถเปลี่ยนแปลงได้** หลังสร้าง

ตารางเปรียบเทียบ

ลำดับหัวข้อMutable ObjectImmutable Object
1พื้นฐานแก้ไข state ได้หลังสร้าง (setters, public fields)ไม่สามารถแก้ไข state ได้หลังสร้าง
2Thread Safetyไม่ thread-safe (ต้อง synchronize เอง)Thread-safe โดยธรรมชาติ (immutable = safe)
3Final Classไม่จำเป็นต้อง finalควรทำ class final (ป้องกัน subclass แก้ไข)
4Fieldsมี setter methods / non-final fieldsfields ทั้งหมดเป็น private final
5ตัวอย่างArrayList, StringBuilder, Custom classes with settersString, Integer, LocalDate, Custom immutable classes
6Defensive Copyไม่จำเป็น (แต่ควรระวัง)จำเป็นสำหรับ mutable fields (Date, Collections)
7HashCode/Equalsอาจเปลี่ยนได้ (อันตรายใน HashMap/HashSet)คงที่ตลอดชีวิต (ปลอดภัยเป็น key)

ตัวอย่าง Immutable Class (Best Practice)

public final class ImmutableStudent {
    private final String name;
    private final int age;
    private final List subjects; // mutable object ต้อง defensive copy

    public ImmutableStudent(String name, int age, List subjects) {
        this.name = name;
        this.age = age;
        // Defensive copy - ไม่ให้ภายนอกแก้ list ต้นฉบับได้
        this.subjects = new ArrayList<>(subjects);
    }

    // Getters เท่านั้น (ไม่มี setters)
    public String getName() { return name; }
    public int getAge() { return age; }
    
    // คืน defensive copy ป้องกันการแก้ไขจากภายนอก
    public List getSubjects() { 
        return new ArrayList<>(subjects); 
    }

    // with-er pattern สำหรับ "แก้ไข" (คืน object ใหม่)
    public ImmutableStudent withAge(int newAge) {
        return new ImmutableStudent(this.name, newAge, this.subjects);
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + ", subjects=" + subjects + "}";
    }
}

ตัวอย่าง 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;
    }

    // มี setters - สามารถเปลี่ยน state ได้
    public void setName(String name) { this.name = name; }
    public void setAge(int age) { this.age = age; }
    public void addSubject(String subject) { subjects.add(subject); }
    public List getSubjects() { return subjects; } // อันตราย! คืน reference ตรงๆ

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + ", subjects=" + subjects + "}";
    }
}

การทดสอบ

public class Test {
    public static void main(String[] args) {
        // Immutable
        ImmutableStudent im = new ImmutableStudent("John", 20, List.of("Math", "Physics"));
        System.out.println("Original: " + im);
        ImmutableStudent im2 = im.withAge(21);
        System.out.println("After withAge: " + im2);
        System.out.println("Original unchanged: " + im);
        
        // พยายามแก้ไข (compile error ถ้า uncomment)
        // im.setAge(25); // Error: no setter
        // im.getSubjects().add("Chemistry"); // ไม่ส่งผลต่อ object เดิม (defensive copy)
        
        // Mutable
        MutableStudent mut = new MutableStudent("Jane", 22);
        mut.addSubject("Biology");
        System.out.println("\nMutable: " + mut);
        
        // อันตราย: external modification
        List ref = mut.getSubjects();
        ref.add("Hacked!"); // แก้ไข internal state ได้!
        System.out.println("After external hack: " + mut);
    }
}

Output

Original: Student{name='John', age=20, subjects=[Math, Physics]}
After withAge: Student{name='John', age=21, subjects=[Math, Physics]}
Original unchanged: Student{name='John', age=20, subjects=[Math, Physics]}

Mutable: Student{name='Jane', age=22, subjects=[Biology]}
After external hack: Student{name='Jane', age=22, subjects=[Biology, Hacked!]}

ทำไม Immutable ดีกว่า?

  • ✅ **Thread-safe** ใช้ร่วมกันได้โดยไม่ต้อง lock
  • ✅ **Predictable** state ไม่เปลี่ยน → debug ง่าย
  • ✅ **Safe as Map/Set key** hashCode ไม่เปลี่ยน
  • ✅ **Failure atomicity** ถ้า exception ขึ้น object ไม่ corrupt
  • ✅ **Cache-friendly** ใช้ซ้ำได้ (String pool, Integer cache)

เมื่อไหร่ควรใช้ Mutable

  • Performance critical: หลีกเลี่ยง object allocation เยอะ (game loop, high-frequency trading)
  • Large data structures: สร้างใหม่ทุกครั้งแพงเกิน (ใช้ persistent data structures แทน)
  • JavaBeans / Framework requirement: บาง framework ต้องการ setters (Hibernate, Jackson)
  • Builder pattern: mutable builder → immutable result

Immutable Classes ใน JDK

  • String, جميع Wrapper classes (Integer, Long, Double, ...)
  • java.time.* (LocalDate, LocalDateTime, Instant, ...)
  • BigInteger, BigDecimal
  • UUID, Class, StackTraceElement