ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] 불변 객체
    자바 2024. 8. 19. 13:27

    아직까지 실무에서 불변 객체 사용할 일이 없었지만 언젠간 마주칠 것 같아 정리한다.

     

     

    자바의 데이터 타입은 크게 기본형과 참조형으로 나눌 수 있다.

    조심해야 할 점은 참조형의 공유 참조로 인한 사이드 이펙트이다.

    ( 사이드 이펙트(Side Effect)는 프로그래밍에서 어떤 계산이 주된 작업 외에 추가적인 부수 효과를 일으키는 것 )

     

     

    public class RefMain1_1 {
        public static void main(String[] args) {
        
        // 참조형 변수는 하나의 인스턴스를 공유할 수 있다.
        Address a = new Address("서울");
        Address b = a;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        // a = Address{value='서울'}
        // b = Address{value='서울'}
        
        b.setValue("부산"); //b의 값을 부산으로 변경해야함
        System.out.println("a = " + a); //사이드 이펙트 발생
        System.out.println("b = " + b);
        // a = Address{value='부산'}
        // b = Address{value='부산'}
        
        }
    }

    참조형 변수는 하나의 인스턴스를 공유하기 때문에 b.setValue로 값을 변경해 주면 똑같은 참조 주소를 갖고 있는 a, b의 값 모두 변경된다. 이때 사이드이펙트가 발생한다.

     

     

    사이드 이펙트 해결 방안

    ddress a = new Address("서울");
    Address b = new Address("서울");

    a와 b가 같은 객체를 공유하지 않도록 서로 다른 인스턴스를 참조하면 된다.

    ** 참조값의 공유를 막을 수 있는 방법이 없다.

     

    혹은 설계 자체를 변경할 수 없도록 하면 된다.

    이때 사용되는 것이 불변 객체이다.

     

     

    불변 객체

    객체의 상태(객체 내부의 값, 필드, 멤버 변수)가 변하지 않는 객체를 불변 객체(Immutable Object)라 한다.

    public class ImmutableAddress {
        
        private final String value;
        
        public ImmutableAddress(String value) {
            this.value = value;
        }
        
        public String getValue() {
            return value;
        }
        
         @Override
         public String toString() {
             return "Address{" + "value='" + value + '\'' + '}';
         }
         
    }

    - 내부 값이 변경되면 안 되기 때문에 value의 필드를 final로 선언했다.

    - 값을 변경할 수 있는 setValue() 를 제거했다.

    - 이 클래스는 생성자를 통해서만 값을 설정할 수 있고, 이후에는 값을 변경하는 것이 불가능하다.

     

    처음 코드를 봤을 때 단순하게 변경하면 안 되니까 final 키워드를 사용했구나 그래서 불변객체네라고 생각했다.

    하지만 여기서 불변객체를 만들어준 가장 중요한 요소는 setValue 코드가 삭제 되었다는 점이었다.

    final 은 그렇게 큰 영향을 주지 않았다.

     

     

    public class RefMain2 {
        public static void main(String[] args) {
     
        ImmutableAddress a = new ImmutableAddress("서울");
        ImmutableAddress b = a; //참조값 대입을 막을 수 있는 방법이 없다.
        System.out.println("a = " + a);
        System.out.println("b = " + b);
     
        //b.setValue("부산"); //컴파일 오류 발생
        b = new ImmutableAddress("부산");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        
        }
    }

    - ImmutableAddress 인스턴스의 값을 변경할 수 있는 방법은 없다.

    - 따라서 새로운 ImmutableAddress("부산") 인스턴스를 생성해서 b에 대입한다.

    - 결과적으로 a , b 는 서로 다른 인스턴스를 참조하고, a 가 참조하던 ImmutableAddress는 그대로 유지된다.

     

     

    불변 객체 - 값 변경

    ( 불변 객체를 사용하지만 그래도 값을 변경해야 하는 메서드가 필요한 경우)

    public class ImmutableObj {
    
        private final int value;
     
        public ImmutableObj(int value) {
            this.value = value;
        }
        
        public ImmutableObj add(int addValue) {
            int result = value + addValue;
            return new ImmutableObj(result);
        }
        
        public int getValue() {
            return value;
        }
        
    }

    add() 메서드를 보면 기존 객체의 값을 그대로 두고 대신에 변경된 결과를 새로운 객체에 담아서 반환하고 있다.

     

     

    public class ImmutableMain1 {
        public static void main(String[] args) {
        
            ImmutableObj obj1 = new ImmutableObj(10);
            ImmutableObj obj2 = obj1.add(20);
            
            //계산 이후에도 기존값과 신규값 모두 확인 가능
            System.out.println("obj1 = " + obj1.getValue());
            // obj1 = 10
            System.out.println("obj2 = " + obj2.getValue());
            // obj2 = 30
            
        }
    }

     

     

    클래스를 불변으로 설계하는 이유

    1) 캐시 안정성

    2) 멀티 쓰레드 안정성

    3) 엔티티의 값 타입

     

     

    cf. String은 불변객체

    String 은 불변 객체이다. 따라서 생성 이후에 절대로 내부의 문자열 값을 변경할 수 없다.

    String이 불변으로 설계된 이유?

    String 은 자바 내부에서 문자열 풀을 통해 최적화를 한다. 만약 String 내부의 값을 변경할 수 있다면, 기존에 문자열 풀에서 같은 문자를 참조하는 변수의 모든 문자가 함께 변경되어 버리는 문제가 발생한다. (사이드 이펙트 문제)

     

     

     

     

     


    (참고)

    김영한의 실전자바 - 중급 1편 (섹션2. 불변객체)

    김영한의 실전자바 - 중급 1편 (섹션3. String 클래스)

    728x90

    '자바' 카테고리의 다른 글

    [JAVA] Int & Integer 차이점 그리고 wrapper 클래스  (0) 2024.08.22
    [JAVA] Static 키워드  (0) 2024.08.20
    [JAVA] equals() - 동일성과 동등성  (0) 2024.08.19
    [JAVA] Object 클래스  (0) 2024.08.19
    [JAVA] 생성자  (0) 2024.08.14
Designed by Tistory.