-
[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