ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바 불변 객체와 final에 대한 개념
    Java 2023. 10. 9. 17:56

    불변 객체(Immutable Object)

    • 객체 생성 이후 내부의 상태가 변하지 않는 객체
    • read-only메소드만 제공하고, 내부 상태를 제공하는 메소드를 제공하지 않거나 방어적 복사를 통해 제공
      • 방어적 복사 : 참조를 통해 값을 수정하면 내부의 상태가 변하기 때문에 내부를 복사하여 전달하는 것

     

    불변 객체와 final을 사용하는 이유

    1. Thread-Safe하여 병렬 프로그래밍에 유용하고 동기화를 고려하지 않아도 된다.
      • 멀티 쓰레드 환경에서 공유자원을 동시에 쓰기(Write) 때문에 동기화 문제가 발생한다. 하지만, 공유 자원이 불변이라면 더 이상 동기화를 고려하지 않아도 된다. (항상 동일한 값을 반환하기 때문)
      • 안정성을 보장하고 동기화를 하지 않아 성능상의 이점을 가져온다.
    2. 실패 원자적인 메소드를 만들 수 있다.
      • 가변 객체의 경우 예외가 발생하면 해당 객체는 불안정한 상태로 에러를 발생할 수 있다. 하지만, 불변 객체는 예외가 발생해도 메소드 호출 전 상태를 유지할 뿐 아니라 에러가 발생하지 않은 것처럼 다음 로직 수행이 가능하다.
    3. Cache나 Map 또는 Set 등의 요소로 활용하기에 더욱 적합하다.
      • Cache나 Map 또는 Set인 객체가 가변이라면 변경되었을 때, 이를 갱신하는 부가 작업이 필요하다. 하지만 불변이라면 한번 저장된 객체는 다른 작업을 고려하지 않아도 된다.
    4. side effect를 피해 오류 가능성을 최소화할 수 있다.
      • 객체의 setter를 통해 객체를 변경한다면 바뀐 상태를 파악하기 위해 메소드들을 살펴봐야하고 유지보수성을 떨어트린다.
      • 불변 객체의 경우에는 수정이 불가능해 순수 함수로 구성이 되고 호출이 되어도 상태가 유지되어 안전하게 객체를 다시 사용할 수 있다. 따라서, 유지보수성이 높은 코드를 작성할 수 있게 도와준다.
    5. 다른 사람이 작성한 함수를 예측 가능하며 안전하게 사용 가능
      • 불변성이 보장된 함수라면 다른 사람이 개발한 함수를 위험없이 이용할 수 있다. 그래서 변경에 대한 불안 없이 다른 사람의 코드를 사용하고 불필요한 시간도 절약이 가능하다.
    6. 가비지 컬렉션의 성능을 높인다.
      • 불변의 객체를 생성하면 컨테이너 객체가 이를 참조할 수 있다. (컨테이너 객체가 더 늦게 생성)
      • 가비지 컬렉터가 컨테이너 객체 하위의 불변 객체들은 처음에 할당된 상태 그대로 있기 때문에 스캔을 하지 않는다.
        • GC 수행의 객체 수가 줄어들어 메모리 영역과 빈도수 역시 줄어든다.
        • GC 수행되어도 지연 시간을 줄인다.
        • 필드값을 수정할 수 있는 MutableHolder보다는 필드값을 수정할 수 없는 ImmutableHolder를 사용하는 것이 좋다. (MutableHolder의 값이 지속되어 old-to-young 참조가 일어나는 것이 더 큰 성능 저하를 야기)

     

    불변 객체 생성 방법

    • 마지막으로 클래스의 변수에 가능하다면 final
    • final이 불가능하다면 Setter를 최소화
    final String str = "before";
    str = "after";  // 컴파일 에러
    
    // 새로운 객체가 더해져도 문제가 없다. 그래서 불변 클래스로 만들어야한다.
    final List<String> list = new ArrayList<>();
    list.add("a");
    • 불변 클래스 생성 규칙
      • 클래스를 final로 선언
      • 모든 클래스 변수를 private과 final로 선언
      • 객체를 생성하기 위한 생성자 또는 정적 팩토리 메소드를 추가
      • 참조에 의해 변경가능성이 있는 경우 방어적 복사를 이용하여 전달
    public final class ImmutableClass {
        private final int age;
        private final String name;
        private final List<String> list;
    
        // 내부 생성자를 만드는 대신 객체의 생성을 위해 정적 팩토리 메소드를 제공
        private ImmutableClass(int age, String name) {
            this.age = age;
            this.name = name;
            this.list = new ArrayList<>();
        }
    
        public static ImmutableClass of(int age, String name) {
            return new ImmutableClass(age, name);
        }
        
        public int getAge() {
            return age;
        }
    
        public String getName() {
            return name;
        }
        // 참조를 전달하여 클라이언트에 의해 수정가능성이 있는 list를 방어적 복사하여 제공
        public List<String> getList() {
            return Collections.unmodifiableList(list);
        }
        
    }

     
     
    출처)
    https://mangkyu.tistory.com/131

Designed by Tistory.