[Java]불변객체
불변 객체
불변 객체의 등장 배경 - 공유 참조와 사이드 이펙트
자바에서 데이터 타입을 가장 크게 보면 기본형(Primitive Type)과 참조형(Reference Type)으로 나눌 수 있다.
- 기본형 : 하나의 값을 여러 변수에 절대로 공유하지 않는다.
- 참조형 : 하나의 객체를 참조값을 통해 여러 변수에서 공유할 수 있다. → 사이드 이펙트를 발생시킨다.
Address a = new Address("서울");
Address b = a;
b.setValue("부산");
위 코드에서 Address 객체 a, b를 생성하고 b=a라는 코드를 통해, b는 a에 있는 참조값을 복사해서 전달받게 된다. 결국 이 과정을 통해 a와 b는 메모리 영역에 있는 동일한 주소(참조값)를 바라보게 되는 것이다. 그렇기 때문에 바꾸고 싶었던 값은 b지만, a의 값이 함께 변경되는 ‘사이드 이펙트‘가 발생하게 되는 것이다. 이러한 사이드 이펙트는 디버깅을 어렵게하고 코드의 안정성을 저하시킨다. 그렇다면 어떻게 이러한 사이드 이펙트를 방지할 수 있을까?
객체 a와 b가 처음부터 각각 서로 다른 인스턴스를 참조하면 되는것이다.
Address a = new Address("서울");
Address b = new Address("서울");
b.setValue("부산");
위와 같이 a와 b가 각각 다른 인스턴스를 참조하고 있는 경우, b의 값을 바꾼다고 하여 a의 값이 바뀌는 일은 없다. 하지만 여기서 문제가 발생하는데 문제가 되는 코드 상황처럼 하나의 객체를 여러 변수가 공유하지 않도록 강제하는 방법은 없다는 것이다. 다시 말하자면, 기본형은 항상 값을 복사해서 대입하기 때문에 값이 절대로 공유되지 않는다. 하지만 참조형의 경우 참조값을 복사해서 대입하기 때문에 여러 변수에서 얼마든지 같은 객체를 공유할 수 있다. 그리고 이는 문법적인 오류가 없기 때문에 이를 강제로 제한할 수 없는 것이다.
불변 객체의 도입
문제는 객체가 여러 변수를 공유하기 때문에 일어났다. 하지만 더 근본적인 원인을 보자. 단순히 공유만으로 문제가 발생한 걸까? 아니다. 문제의 직접적인 원인은 공유한 객체의 값을 변경한 것에 있다. 사실 처음에 a,b가 처음에 둘 다 같은 값을 사용해야 한다는 조건이 있었다면, 각각의 인스턴스를 생성하는 것보다 Address b=a
와 같이 하나의 Address 인스턴스를 a,b가 함께 사용하는 것이 서로 다른 인스턴스를 사용하는 것보다 메모리와 성능상 더 효율적이다. 진짜 문제는 이후에 b가 공유 참조하는 인스턴스의 값을 변경하기 때문에 발생한 것이다. **즉, 만약 Address 객체의 값이 변경될 수 없도록 설계되었다면 이런 사이드 이팩트는 발생하지 않을 수 있던 것이다.**
불변객체로 만드는 방법은 무엇일까?
public class Address {
private final String value;
public Address(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
내부값이 변경되면 안되기 때문에 Address 클래스의 필드에 final
변수를 활용한다. 또한 get/set 메서드 중 외부에서 값을 변경할 가능성이 있는 set()메서드는 만들지 않는다. 이 클래스는 생성자를 통해서만 값을 설정할 수 있고 이후에 값을 변경하는 것은 불가능하다. 이렇듯 어떤 방식으로든 클래스의 필드값을 변경할 수 없게 설계한 클래스가 곧 불변 객체이다.
불변 객체 - 값 변경
불변 객체를 사용하지만 그래도 값을 변경해야 하는 메서드가 필요한 상황엔 어떻게 해야할까?
public class ImmutableObj{
private final int value;
public InnutableObj(int value){
this.value = value;
}
public ImmutableObj add(int addValue){
return new ImmutableObj(value + addValue);
}
}
불변 객체는 값을 변경하면 안되는데, add()메서드 처럼 기존 값에 새로운 값을 더한 값을 반환해야하는 경우, 위와 같이 불변 객체를 새로 만들어 객체를 생성 후 생성 된 객체를 반환하는 방식을 택한다. 즉 불변 객체를 설계할 때 기존 값을 변경해야 하는 메서드가 필요하다면, 기존 객체의 값을 그대로 두고 대신에 변경된 결과를 새로운 객체에 담아서 반환하도록 객체를 설계하도록 해야하는 것이다.
✅ 불변 객체는 왜 중요한걸까?
자바에서 가장 많이 사용되는 String
클래스가 바로 불변객체이기 때문이다. 뿐만 아니라 자바가 기본으로 제공하는 Integer
, LocalDate
등 수많은 클래스가 불변으로 설계되어 있기 때문에 불변 객체가 필요한 이유와 원리를 제대로 이해해야 이러한 자바의 기본 클래스들을 제대로 이해할 수 있다.
모든 클래스를 불변으로 만들 필요는 없으며, 우리가 만든느 대부분의 클래스는 값을 변경할 수 있는 가변 클래스이다. 그럼에도 불구하고 불변 클래스를 쓰는 경우는 다음과 같다.
- 캐시 안정성
- 멀티 쓰레드 안정성
- 엔티티의 값 타입
Leave a comment