새소식

TIL

[TIL] 240510 불변 객체(Immutable Object)

  • -

 

불변 객체 (Immutable Object)

 

공유된 참조 객체의 값을 변경하여 발생하는 문제들을 해결 가능

→ 객체가 가지고 있는 필드(멤버 변수)를 변경할 수 없도록 설계

 

public class ImmutableReference {

    private final String name;  //final로 선언하여 값 변경 불가

	// 생성자만 값설정 가능
    public ImmutableReference(String name) {
        this.name= name;
    }

    public String getName() {
        return name;
    }

    // final 키워드로 값의 변경이 불가능하여 해당 메서드 사용불가
//    public void setName(String name) {
//        this.name = name;
//    }
}
  • 오직 생성자만 값을 설정 가능
  • final 키워드를 사용하여 값 변경 불가
  • final을 사용하지 않아도, 값들이 바뀌는것이 불가능하다면 불변 객체임
    ㄴ setter를 사용하지 않는 경우

 

public class ImmutableMain {

    public static void main(String[] args) {

        ImmutableReference reference1 = new ImmutableReference("황원욱");
        ImmutableReference reference2 = reference1; //둘은 같은 주소값 가짐
        // stack 영역에 주소값이 저장된다.

        // 변경 불가
//        reference2.setName("홍정기");

    }
}
  • final 필드는 set불가

 

public class SpartaV2 {

    private String id;
    
    private ImmutableReference reference;  // 불변 객체 사용

    public SpartaV2(String id, ImmutableReference reference) {
        this.id = id;
        this.reference = reference;
    }

    public ImmutableReference getImmutableReference() {
        return reference;
    }
    
    // 불변 객체를 필드로 가지는 다른 클래스에서의 수정 set은 가능.
    public void setReference(ImmutableReference reference) {
        this.reference = reference;
    }
}
  • 불변 객체를 필드로 가지는 다른 클래스에서의 set은 가능 (여기서 불변 객체를 final로 선언하지는 않았으므로!)

 

public class ImmutableMain2 {

   public static void main(String[] args) {
   		// 불변 객체 생성
        ImmutableReference reference = new ImmutableReference("황원욱");
        
        SpartaV2 sparta1 = new SpartaV2("1", reference);
        SpartaV2 sparta2 = new SpartaV2("2", reference);

        // sparta 인스턴스가 가지고 있는 Reference 객체의 값 출력
        System.out.println("------- 인스턴스에 저장된 객체 값 확인 -------");
        System.out.println("sparta1.referenceName = " + sparta1.getImmutableReference().getName());
        System.out.println("sparta2.referenceName = " + sparta2.getImmutableReference().getName());

        // sparta2의 Reference에 저장된 값 변경 불가
//        sparta2.getReference().setName("홍정기");
				
        // 변경하고 싶다면 새로운 인스턴스를 생성하여 reference 필드 자체를 set으로 변경
		// 새로운 "홍정기" 이름의 불변 객체 생성
        ImmutableReference reference2 = new ImmutableReference("홍정기");
        // SpartaV2 클래스의 인스턴스인 sparta2의 reference 필드 자체는 set으로 변경 가능
        sparta2.setReference(reference2);

        System.out.println("------- 변경 후 값 확인 -------");
        System.out.println("sparta1.referenceName = " + sparta1.getImmutableReference().getName());
        System.out.println("sparta2.referenceName = " + sparta2.getImmutableReference().getName());

    }

}
  • sparta2의 Reference에 저장된 값을 변경하고 싶다면 새로운 불변 객체 인스턴스를 생성하여 sparta2의 reference 필드를 set해야함

 

 

불변 객체를 변경해야하는 경우

public class ImmutableReference {

    private final String name;

		// 생성자
    public ImmutableReference(String name) {
        this.name= name;
    }

    public String getName() {
        return name;
    }

    // 새로운 객체를 생성하여 반환 한다.
    public ImmutableReference withName(String name) {
	    return new ImmutableReference("name");
    }

}
  • 새로운 인스턴스를 생성하여 반환
    ㄴ 기존 인스턴스의 값은 그대로 유지됨
  • 불변 객체의 값을 변경하는 메서드는 with이름() 형태로 사용하는것이 일반적
    ㄴ 기존 인스턴스 + 새로운 인스턴스 라는 의미

 

 


String은 불변 객체이다

String은 참조형에 속하지만 기본형처럼 사용

  • String 뿐만이 아니라 Integer와 같이 다양한 클래스들이 불변 객체이다.
  • 가변 String 객체는 StringBuilder

  • String 클래스에는 값을 변경해주는 메소드들이 존재하지만 해당 메소드를 통해 데이터를 바꾼다 해도
    새로운 String 클래스 객체를 만들어내는 것
    • 일반적으로 기본형 비교는 == 연산자를 사용하지만
      String 객체간의 비교는 .equals() 메소드를 사용하는 이유
    • equals() 메서드 뿐만 아니라 다른 값 변경 메서드(concat 등)도 동일하게 새로운 인스턴스를 생성함

 


 

장점

  • 불변 객체를 사용하면 해당 인스턴스에 대한 신뢰도가 생김
    ㄴ 값이 변경되지 않기 때문에 믿고 사용 가능
  • Thread-safe 
    ㄴ 동일한 값을 가지고 있어 동기화를 신경쓰지 않아도 됨

 

단점 

  • 불변 객체에 새로운 값이 필요할 때마다 새로운 객체 생성됨
    ㄴ 리소스 소모
    ㄴ 하지만, GC가 사용되지 않는 객체는 메모리에서 삭제하기 때문에 괜찮음
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.