Java - Immutable Object

develkkm·2025년 11월 11일

0부터 시작하는 Java

목록 보기
22/27

불변 객체

이 글은 자바에서 불변 객체(Immutable Object)의 개념과 필요성을 정리한 것이다.

기본형과 참조형의 차이, 공유 참조와 사이드 이펙트, 불변 객체의 설계와 값 변경 방식, 그리고 불변 객체가 사용되는 이유와 String 클래스와의 관계를 순서대로 설명한다.


기본형과 참조형

자바의 데이터 타입은 기본형(Primitive Type)참조형(Reference Type)으로 구분된다.
기본형은 하나의 값을 직접 저장하며, 참조형은 객체의 주소값을 저장한다.

기본형 예시

기본형 변수는 값을 복사하므로 절대로 값을 공유하지 않는다.

int a = 10;
int b = a;
b = 20;
System.out.println("a = " + a);
System.out.println("b = " + b);

실행 결과

a = 10
b = 20

b의 값을 변경해도 a는 그대로 유지된다.
기본형은 항상 독립된 값을 가진다.

참조형 예시

참조형 변수는 객체의 주소값을 저장하므로, 하나의 객체를 여러 변수가 공유할 수 있다.

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

public class RefMain {
    public static void main(String[] args) {
        Address a = new Address("서울");
        Address b = a;
        b.setValue("부산");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

실행 결과

a = Address{value='부산'}
b = Address{value='부산'}

두 변수가 같은 객체를 공유하므로 한쪽의 변경이 다른 쪽에도 반영된다.


공유 참조와 사이드 이펙트

공유 참조란 여러 변수가 같은 객체를 참조하는 현상이다.
이때 한 변수가 객체의 상태를 변경하면 다른 변수도 영향을 받는다.
이와 같은 부수 효과를 사이드 이펙트(Side Effect)라 한다.

예시

Address a = new Address("서울");
Address b = a;
b.setValue("부산");
System.out.println("a = " + a);
System.out.println("b = " + b);

실행 결과

a = Address{value='부산'}
b = Address{value='부산'}

변수 b의 값만 변경했지만, a의 값도 함께 변경되는 부작용이 발생한다.

해결 방안

사이드 이펙트를 방지하기 위해서는 서로 다른 객체를 참조해야 한다.

Address a = new Address("서울");
Address b = new Address("서울");
b.setValue("부산");
System.out.println("a = " + a);
System.out.println("b = " + b);

실행 결과

a = Address{value='서울'}
b = Address{value='부산'}

이 경우에는 각 변수가 독립적인 객체를 참조하므로 영향을 주지 않는다.

그러나 자바 문법상 Address b = a;와 같은 참조값 복사를 막을 수는 없다.
따라서 공유 참조 자체를 원천적으로 차단할 수 있는 방법은 없다.


불변 객체

불변 객체 설명

공유 참조를 막을 수 없다면, 객체의 상태가 변경되지 않도록 설계해야 한다.
이처럼 내부 상태가 한 번 정해지면 이후 절대 바뀌지 않는 객체를 불변 객체(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 + '\'' + '}';
    }
}

이 클래스는 final 필드를 사용하고 setValue() 메서드를 제거하여 값을 변경할 수 없게 만들었다.

불변 객체 예시

public class RefMain2 {
    public static void main(String[] args) {
        ImmutableAddress a = new ImmutableAddress("서울");
        ImmutableAddress b = a;
        b = new ImmutableAddress("부산");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

실행 결과

a = Address{value='서울'}
b = Address{value='부산'}

b의 값을 변경하려면 새로운 객체를 만들어야 한다.
기존 a는 영향을 받지 않으며, 사이드 이펙트가 발생하지 않는다.


불변 객체의 값 변경 예시

불변 객체는 내부 상태를 직접 변경하지 않는다.
값을 변경해야 하는 경우, 변경된 값을 가진 새로운 객체를 반환한다.

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;
    }
}
public class ImmutableMain {
    public static void main(String[] args) {
        ImmutableObj obj1 = new ImmutableObj(10);
        ImmutableObj obj2 = obj1.add(20);
        System.out.println("obj1 = " + obj1.getValue());
        System.out.println("obj2 = " + obj2.getValue());
    }
}

실행 결과

obj1 = 10
obj2 = 30

add()는 기존 객체를 변경하지 않고, 새로운 객체를 만들어 반환한다.
이로써 불변성을 유지하면서 값의 변화를 표현할 수 있다.


불변 객체와 String 클래스

불변 객체는 다음과 같은 이유로 사용된다.

  1. 사이드 이펙트 방지
    여러 변수가 하나의 객체를 공유하더라도 내부 상태가 변경되지 않아 예측 가능한 동작을 보장한다.

  2. 멀티스레드 안정성
    객체가 변경되지 않기 때문에 동기화 문제 없이 여러 스레드에서 안전하게 공유할 수 있다.

  3. 캐싱 안정성
    불변 객체는 동일한 상태라면 재사용이 가능하여 캐시 효율이 높다.

자바의 대표적인 불변 클래스가 String이다.
String은 내부적으로 final char[] 배열을 사용하며, 문자열의 연결이나 변경 연산 시 항상 새로운 String 객체를 생성한다.

String a = "Hello";
String b = a;
b = b + " World";
System.out.println(a);
System.out.println(b);

실행 결과

Hello
Hello World

b를 변경해도 a의 값은 그대로 유지된다.
이는 String이 불변 객체이기 때문에 가능한 동작이다.

profile
알던것을 더 확실하게

0개의 댓글