[TIL] 20250102(2) 불변 객체

Drumj·2025년 1월 2일
0

2025 TIL

목록 보기
3/11

공유 참조와 사이드 이펙트

불변 객체를 알아보기 전에 불변 객체가 왜 필요한지 간단하게 알아보자.

//기본형
int a = 10;
int b = a;

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

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

//참조형
Address a = new Adderss("서울");
Address b = a;

System.out.println("a = " + a); // a = "서울"
System.out.println("b = " + b); // b = "서울"

b.setValue("대구");
System.out.println("a = " + a); // a = "대구" , 사이드 이펙트 발생!
System.out.println("b = " + b); // b = "대구"

위 코드를 보면 알 수 있듯이
기본형의 경우에는 a의 값(10) 을 복사해서 b에 대입하기 때문에 b를 변경할 경우 b의 값만 변경되지만
참조형의 경우에는 a의 참조값을 복사해서 b에 대입하기 때문에 b를 변경해도 a가 변경된다.

이를 해결하기 위해서 아래와 같이 a와 b를 작성할 수 있다.

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

b.setValue("대구")

이렇게 할 경우 내가 원하는대로 b의 값만 대구로 변경된다. 하지만 이렇게 작성을 하더라도 하나의 객체를 공유하는 것을 막을 방법이 없다.

즉 내가 원하는 코드 작성 방식은 객체를 공유하지 않도록

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

이렇게 각각 a,b 를 만드는 것이지만 누군가는

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

이렇게 작성을 할 수도 있는 것이다. 물론 a,b 모두 서울이라는 주소를 가져야 한다면 b = a 코드가 더 효율적이긴 하다.

사실 이렇게 공유를 하는것 자체는 사이드 이펙트가 발생하지 않는다. 문제는 이렇게 공유한 객체의 값을 변경하는 것이다.

b.setValue("대구"); // a를 공유한 b가 set을 사용해서 값을 변경하는게 문제다!

불변 객체

객체의 공유 참조는 막을 수 없다는 것을 위의 예시를 통해 알았다. 그래서 객체의 값을 변경할 수 없도록 불변 객체를 만들어서 사용하면 사이드 이펙트를 원천 차단할 수 있다.

// 불변 객체 생성
public class Address {
    private final String value; // final 을 사용
    
    public Address(String value) {
        this.value = value;
    }
    
    public String getValue() {
        return value;
    }
    
    //setter 가 없음!
    
    @Override
    public String toString() {
        return "Address{" +
                "value='" + value + '\'' +
                '}';
    }
}

위 클래스를 보면 value 필드를 final로 선언했고 setValue() 메서드도 존재하지 않는다.
이제 value 값을 변경할 수 있는 방법이 존재하지 않는다는 것.

Address a = new Address("서울");
Address b = a; // 참조값 대입을 막을 수 있는 방법은 없다.

System.out.println("a = " + a); // a = Address{value='서울'}
System.out.println("b = " + b); // b = Address{value='서울'}

//b.setValue("대구"); //불변 객체에는 setter 메서드가 없다.
b = new Address("부산"); //새로운 인스턴스 생성
System.out.println("a = " + a); // a = Address{value='서울'}
System.out.println("b = " + b); // b = Address{value='부산'}

b의 값을 변경하기 위해 setValue() 를 사용하려 했으나 Address 클래스에는 해당 메서드가 존재하지 않는다. 어쩔 수 없이 new Address("부산") 새로운 인스턴스를 생성할 수 밖에 없다.


record

또 Java14 에서 등장한 record 를 사용해서 쉽게 불변 객체를 만들수도 있다.

public record class Address(String value) {}

위의 Address 코드를 record 클래스로 바꾼 것이다.

해당 클래스의 모든 필드가 final로 선언되어 있고 각각 필드의 getter를 가지고 있다면 record 클래스로 변경할 수 있다.

또한 record는

생성자, getter, hashCode(), equals() ,toString() 메서드를 기본으로 제공한다.

그리고 getter의 경우에는 getValue() 와 같은 형식이 아닌 value() 로 사용한다.

Address a = new Address("서울");
//a.getValue();
a.value();

간단하게 record 를 알아봤는데 더 자세히 알고 싶다면 미스터포포님의 블로그를 읽어보면 좋을 것 같다.


참조

0개의 댓글