Item 17. 변경 가능성을 최소화하라

불변 클래스

불변 클래스의 안정성

  • 객체가 파괴(회수)될 순간까지 객체 내부의 값이 절대 달라지지 않음

  • 가변 클래스에 비해 설계와 구현이 쉬우며, 오류가 덜 생김

  • 불변 객체는 스레드 안정성(Thread-Safe)를 보장

    • 스레드 안정성은 여러 스레드가 하나의 리소스를 동시에 사용하며 상태를 변경시키게 되면 깨지는데, 불변 객체는 애초에 상태가 변경될 일이 없어 여러 스레드에서 동시에 사용해도 문제가 없음

    • 따라서, 불변 객체는 멀티 스레드 환경에서 안심하고 사용할 수 있음

  • 불변 객체는 실패 원자성을 제공

    • 상태가 변하지 않기 때문에, 일부 로직의 실패에 의해 객체가 불일치 상태에 빠질 염려가 없음

불변 클래스를 구현하는 방법

  • 객체의 상태를 변경하는 메서드(변경자)를 제공하지 않아야 함

  • 하위 클래스에서 객체를 변경하는 것을 막기 위해, 확장 불가능 클래스로 선언

    • 이를 달성하기 위한 두 가지 방법이 있음

      1. final 클래스로 선언

      2. private 생성자를 명시하고 정적 팩토리 메서드를 제공

  • 모든 필드를 private으로 선언해 외부에서 이를 수정하는 것을 방지

    • 불변과는 연관이 없으나, public으로 선언하면 외부에 노출되므로 추후 내부 표현을 바꿀 수 없음

  • 모든 필드를 final로 선언해 생성자 호출 시점 이후의 값 변경을 막음

  • 클래스 내부를 제외하고, 클래스 내부의 필드가 참조하는 가변 객체로의 접근을 막음

불변 클래스를 사용할 때 주의할 점

  • 불변 클래스는 객체의 상태가 불변임을 보장해 안정성 면에서는 좋은 선택이지만, 약간의 수정이 필요한 경우에도 새 객체를 만들어야 하기 때문에 성능 문제를 일으킬 수 있음

  • 이를 예방하기 위해서는 최대한 객체를 재사용해야 함

  • 이를 위해 정적 팩토리 메서드(Item 1)과 연관지어, 생성자를 통해 인스턴스를 획득하는 대신 정적 팩토리를 이용하고 이 메서드 내부에서 캐싱을 이용하는 것도 좋음

  • 새 객체를 얻기 위해 여러 번의 상태 변환을 거쳐야 해서 여러 번의 중간 단계 객체가 생길 수 있는 경우에는 이들을 하나의 단계로 묶어서 처리하는 것이 좋음

  • 불변 객체는 값이 변경되지 않기 때문에 불변 객체 간에 같은 내부 데이터를 공유해도 문제가 없음

  • 불변 클래스는 복사 메서드를 제공할 필요가 없음

    • 복사하는 대신 자기 자신을 이용하면 되기 때문

핵심 정리

  • 불변 클래스는 안정성 면에서 뛰어남

  • 불변 클래스는 성능 이슈를 야기할 수 있으나 정적 팩토리 메서드를 제공하거나 캐싱을 도입하는 등의 방법으로 일부 예방이 가능

  • 불변으로 만드는 것이 가장 좋고, 그럴 수 없어 가변으로 만들어야 한다고 해도 최대한 변경 가능한 부분을 최소화하는 것이 안정성 면에서 좋음

    • 합당한 이유가 없다면 필드는 private final으로 선언해 외부에서 접근 및 조작이 불가능하도록 하는 것이 좋음

Last updated