본문 바로가기

개발/Java

불변객체

불변객체 Immutable

불변객체 왜 사용 ? 공유된 객체의 값을 변경한 부분을 잘 제어하기 위해 우리는 불변객체라는 녀석을 사용할 수 있다.

객체의 상태가 변하지 않는 객체를 불변객체(Immutable Object)라고 한다.

 

그럼, 이전 글에서 만들었던 Drink 클래스와는 달리...

2024.08.13 - [분류 전체보기] - 기본형과 참조형

 

기본형과 참조형

우리가 자바의 데이터 타입은 기본형과 참조형으로 나눌 수 있다. 두 타입의 특징은 다음과 같다.**기본형**: 하나의 값을 여러 변수에서 절대로 공유 X**참조형**: 하나의 객체를 참조값을 통해

jinhadev.tistory.com

 

상태가 변하지 않는 ImmutableDrink 불변 클래스를 만들어보자.

package lang.immutable;

public class ImmutableDrink {
    private final String name;

    public ImmutableDrink(String name) {
        this.name = name;
    }

    public String getValue() {
        return name;
    }

    @Override
    public String toString() {
        return "Drink{" +
                "name='" + name + '\'' +
                '}';
    }
}

final로 선언된 name 변수는 이제 setValue method를 사용할 수 없다!

즉 생성자로만 값을 셋팅할 수 있게하고 직접적으로 필드 값을 변경할 수 없게만 해주면 이 자체로 불변객체가 된다는걸 알 수 있다.

package lang.immutable;

public class RefImmutableEx {
    public static void main(String[] args) {
        ImmutableDrink drink1 = new ImmutableDrink("Americano");
        ImmutableDrink drink2 = drink1;
        System.out.println("drink1 = " + drink1);
        System.out.println("drink2 = " + drink2);
        
        // 더이상 drink2를 set 할 수 있는 방법이 없다. ImmutableDirnk Class-> setValue method 없음.
        drink2.setValue("Latte"); // compile Error
    }
}

위에서 ImmutableDrink 클래스에선 이제 값을 변경할 수 있게 set 할 수 있는 방법이 존재하지 않는다. 

왜냐면 여기서 setValue함수는 더이상 없으니 사용하려고하면 컴파일 오류가 발생한다는걸 알 수 있다.

그럼 이렇게 사용하면 얻게되는 장점은 ??

>>> ImmutableDrink 이 클래스 내부를 잘 모르는 개발자도 불변객체라는걸 쉽게 인지 할 수 있다는게 큰 장점이다.

뭐, b = new Address("Latte"); 와 같은 코드로 새로운 객체를 다시 생성해서 임의로 내가 변경할 수는 있다.

 

다만, 내가 사용하려는 객체가 불변 객체인지 아닌지에 따라 불변객체라면 사이드 이펙트가 발생할 여지가 크게 줄어들 수 있다는게 큰 장점이란걸 말해주고 싶다.

 

이번엔 그러면, 날짜를 변경하는 불변 객체를 만들어보자.

package lang.immutable.date;

public class ImmutableMyDate {
    private final int year;
    private final int month;
    private final int day;

    public ImmutableMyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    public ImmutableMyDate withYear(int newYear) {
        return new ImmutableMyDate(newYear, month, day);
    }
    public ImmutableMyDate withMonth(int newMonth) {
        return new ImmutableMyDate(year, newMonth, day);
    }
    public ImmutableMyDate withDay(int newDay) {
        return new ImmutableMyDate(year, month, newDay);
    }

    public ImmutableMyDate withDate(int newYear, int newMonth, int newDay) {
        return new ImmutableMyDate(newYear, newMonth, newDay);
    }

    @Override
    public String toString() {
        return "ImmutableMyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}

ImmutableMyDate Class로 연/월/일을 각각 변경하고 새로운 객체를 리턴하는 함수 withYear, withMonth,withDate 와 연/월/일 한꺼번에 변경할 수 있도록 도와주는 withDate 함수를 만들어 사용해보자.

 

package lang.immutable.date;

public class ImmutableMyDateMain {
    public static void main(String[] args) {
        ImmutableMyDate myDate1 = new ImmutableMyDate(2024, 8, 14);
        ImmutableMyDate myDate2 = myDate1;

        System.out.println("myDate 1 = " + myDate1); // myDate 1 = ImmutableMyDate{year=2024, month=8, day=14}
        System.out.println("myDate 2 = " + myDate2); // myDate 2 = ImmutableMyDate{year=2024, month=8, day=14}

        myDate2 = myDate2.withYear(2025);
        myDate2 = myDate2.withMonth(10);
        myDate2 = myDate2.withDay(30);

        System.out.println("myDate 1 = " + myDate1); // myDate 1 = ImmutableMyDate{year=2024, month=8, day=14}
        System.out.println("myDate 2 = " + myDate2); // myDate 2 = ImmutableMyDate{year=2025, month=10, day=30}

        myDate2 = myDate2.withDate(2030, 01, 05);
        System.out.println("myDate 1 = " + myDate1); // myDate 1 = ImmutableMyDate{year=2024, month=8, day=14}
        System.out.println("myDate 2 = " + myDate2); // myDate 2 = ImmutableMyDate{year=2030, month=1, day=5}
    }
}

println으로 각각 출력한 결과를 살펴보면 변경하지 않은 객체 myDate1은 year=2024, month=8, day=14로 항상 같으며

변경하려고자 하는 myDate2 객체 값은 내가 변경하고자 하는 값으로 잘 변경되는걸 확인할 수 있었다.

 

Java 뿐만아니라, 내가 자주 사용한 언어인 JavaScript도 이 불변객체에 대한 개념을 잘 몰랐을때 이 참조값을 변경하는 문제로 꽤나 힘들었던 경험이 떠오른다. 더군다나 Typescript가 도입되기 이전에 JavaScript는 타입이 존재하지 않으니.. 디버깅하기가 너무 어려웠었다.

'개발 > Java' 카테고리의 다른 글

Enum  (0) 2024.08.15
String Class  (0) 2024.08.14
기본형과 참조형  (0) 2024.08.13
toString 메서드 파헤치기  (3) 2024.08.13
Object 클래스  (0) 2024.08.12