불변객체

✅ 기본형과 참조형의 공유

🔎 도입

프로그래밍에서 변수는 데이터를 저장하는 기본 단위입니다. 하지만 변수의 타입에 따라 데이터의 저장 방식과 공유 방식이 달라집니다. 특히 기본형(Primitive Type)참조형(Reference Type) 은 메모리 관리 방식이 다르며, 이에 따라 값의 변경 및 공유 방식도 차이가 있습니다. 이러한 차이를 이해하는 것은 부수효과(Side Effect)와 불변성(Immutability)의 개념을 파악하는 데 중요한 요소가 됩니다.


🔎 기본형과 참조형의 차이

  • 기본형(Primitive Type)
    • 변수에 값 자체를 저장합니다.
    • 다른 변수에 값을 복사하면 별도의 값이 생성됩니다.
    • 변경해도 다른 변수에는 영향을 주지 않습니다.
  • 참조형(Reference Type)
    • 변수에 객체의 메모리 주소(참조값) 를 저장합니다.
    • 같은 객체를 참조하는 변수를 통해 값을 변경하면, 모든 참조 변수에 영향을 미칩니다.

📝 예제: 기본형과 참조형의 차이 (Java)

public class ReferenceTypeExample {
    public static void main(String[] args) {
        // 기본형 변수
        int a = 10;
        int b = a;  // 값이 복사됨
        b = 20;
        System.out.println("a: " + a); // 10
        System.out.println("b: " + b); // 20

        // 참조형 변수
        int[] arr1 = {1, 2, 3};
        int[] arr2 = arr1; // 참조값(주소)이 복사됨
        arr2[0] = 100;
        System.out.println("arr1[0]: " + arr1[0]); // 100 (같은 객체를 참조)
        System.out.println("arr2[0]: " + arr2[0]); // 100
    }
}

💡 기본형 변수는 값이 복사되지만, 참조형 변수는 객체의 주소를 복사하므로 값 변경이 서로 영향을 줍니다.


✅ 공유참조와 부수효과

🔎 부수효과(Side Effect)란?

  • 부수효과란 함수나 메서드가 주된 작업 외에 추가적인 변경을 일으키는 것을 의미합니다.
  • 대표적인 예로 공유참조(Shared Reference)를 통한 데이터 변경이 있습니다.
  • 부수효과는 예측하지 못한 동작을 초래할 수 있어 디버깅을 어렵게 만듭니다.

📝 예제: 공유참조로 인한 부수효과

class Person {
    String name;

    Person(String name) {
        this.name = name;
    }
}

public class SideEffectExample {
    public static void main(String[] args) {
        Person p1 = new Person("Alice");
        Person p2 = p1;  // p1과 p2가 같은 객체를 참조

        p2.name = "Bob";  // p2를 변경했지만 p1도 영향을 받음
        System.out.println(p1.name); // Bob
    }
}

💡 같은 객체를 참조하는 변수에서 값이 변경되면, 원치 않는 곳에서도 값이 변경되는 문제가 발생할 수 있습니다.


✅ 부수효과의 해결방안

🔎 공유참조 해제

  • 공유참조된 객체의 복사본을 만들어 사용하면, 원본 객체를 보호할 수 있습니다.
  • 하지만 이는 단기적인 해결책이며, 완전한 해결책은 불변객체(Immutable Object)를 활용하는 것입니다.

📝 예제: 객체 복사로 공유참조 해제

class Person {
    String name;

    Person(String name) {
        this.name = name;
    }

    // 복사 생성자 사용
    Person(Person other) {
        this.name = other.name;
    }
}

public class CopyExample {
    public static void main(String[] args) {
        Person p1 = new Person("Alice");
        Person p2 = new Person(p1); // 새로운 객체 생성

        p2.name = "Bob";  
        System.out.println(p1.name); // Alice (영향받지 않음)
    }
}

💡 복사 생성자를 사용하면 공유참조 문제를 완화할 수 있지만, 이는 근본적인 해결책이 아닙니다.


✅ 불변객체(Immutable Object)로 해결

🔎 불변객체란?

  • 한 번 생성되면 내부 상태가 변하지 않는 객체를 의미합니다.
  • Java에서는 final 키워드를 활용하여 불변성을 보장할 수 있습니다.
  • 불변객체는 값이 변경될 때 새로운 객체를 생성하여 반환해야 합니다.

📝 예제: 불변객체 만들기

final class ImmutablePerson {
    private final String name;

    public ImmutablePerson(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    // 새로운 객체 반환 (with 패턴 적용)
    public ImmutablePerson withName(String newName) {
        return new ImmutablePerson(newName);
    }
}

public class ImmutableExample {
    public static void main(String[] args) {
        ImmutablePerson p1 = new ImmutablePerson("Alice");
        ImmutablePerson p2 = p1.withName("Bob"); // 새로운 객체 생성

        System.out.println(p1.getName()); // Alice (원본 객체는 유지)
        System.out.println(p2.getName()); // Bob (새로운 객체)
    }
}

💡 불변객체를 사용하면 원본 객체가 변경되지 않으며, 부수효과를 방지할 수 있습니다.


✅ 불변객체 활용과 관례

🔎 set 대신 with 사용

  • 일반적으로 객체의 속성을 변경할 때 set 메서드를 사용하지만, 불변객체에서는 with를 사용하는 것이 관례입니다.
  • setName(String name) 대신 withName(String name)을 사용하면 새로운 객체를 반환하는 방식임을 명확히 나타낼 수 있습니다.

✅ 결론

  • 기본형과 참조형의 차이를 이해해야 합니다.
  • 공유참조로 인한 부수효과는 디버깅을 어렵게 만듭니다.
  • 불변객체를 활용하면 부수효과를 원천적으로 방지할 수 있습니다.
  • 불변객체에서는 set 대신 with를 사용하여 새로운 객체를 생성하는 방식으로 값을 변경합니다.

💡 객체의 상태를 불변으로 유지하면 코드의 예측 가능성이 높아지고, 유지보수성이 향상됩니다.