가변 객체 vs 불변 객체

mallin·2022년 6월 29일
1

JAVA

목록 보기
6/6
post-thumbnail

가변 객체

먼저, 가변 이란 사전적으로 무슨 의미인지 알아보자 ‼️

가변은 사전적으로 사물의 모양이나 성질이 바뀌거나 달라질 수 있음 을 의미한다.
가변 객체는 사물을 객체로 바꾸면 된다.

즉, 가변 객체는 값을 바꿀 수 있는 객체이다.
여기서 값은 메모리 위치를 위미한다. 가변 객체의 경우에는 값이 변경된다고 하더라고 저장되어 있는 메모리의 위치가 변하지 않는다.

언제든지 값을 바꿀 수 있다 보니 멀티 스레드 환경에서 사용하려면 별도의 동기화 처리가 필요하다.
EX) ArrayList, HashMap, StringBuilder, StringBuffer ... 등등이 이 있다.

코드로 나타내면 아래와 같이 나타낼 수 있는데

x = [1,2,3]
print(id(x)) # 140325471720640
x = [4,5]
print(id(x)) # 140325471720640

x 배열의 값이 변경되어도, 메모리의 위치는 변경되지 않는 걸 확인 할 수 있다.

불변 객체

다음으로 불변 객체에 대해서 알아보자.
이름만 딱 들어도 가변 객체의 반대 개념이겠구나 라는 생각이 팍 든다.

역시 사전적 의미도 가변 과는 정반대로 사물의 모양이나 성질이 변하지 아니함 을 의미한다.
여기도 사물을 객체로 바꾸면 불변 객체는 값을 바꿀 수 없는 객체 이다.

예를 들어서
자바에서 primitive type (boolean, byte, short, int, long, float, double, char) 을 제외한 모든 type 은 reference type (String, Boolean, Integer, Float ... 등등) 인데 해당 타입의 경우 실제 데이터를 힙 영역에 저장하고, 힙 영역의 주소 값을 스택 영역에서 가지고 있는다.

이런 상황 속에서 불변 객체는 힙 영역에 있는 데이터가 변경되지 않는 것을 의미한다.

primitive type 의 불변

primitive type 은 reference type 과 다르게 실제 값을 그대로 stack 에 저장하고 있고, 참조 값이 존재하지 않기 때문에 내부 객체는 불변이다.

하지만, 외부에서 원시 타입에 대해서 변경을 할 수 있는데, 이럴 때에는 Setter 를 생성하지 않으면 된다.

public class Member {
	private int age;
    
    public Member(int age) {
    	this.age = age;
    }
}

위 코드에서 age 는 생성자를 통해서만 설정될 수 있고, 외부에서 다른 방법으로 수정할 방법이 없기 때문에 불변의 형태라고 할 수 있다.

reference type 의 불변

refernce type 의 경우에는 조금 더 복잡하다.

import java.util.List;

public class Cars {
	private final List<Car> cars;
    
    public Cars(List<Car> cars) {
    	this.cars = cars;
    }
}

public class Car {
	private final String name;
    
    public Car(String name) {
    	this.name = name;
    }
}

Cars 는 Setter 와 Getter 가 존재하지는 않지만 불변 객체는 아니다.

Cars 가 생성될 때 인자로 넘어온 리스트를 외부에서 변경하게 되면 Cars 가 참조하고 있는 냅 인스턴스 또한 변경된다.

아래는 불변 객체로 위 예제는 불변 객체로 만드는 코드다.

public class Cars {
	private final List<Car> cars;
    
    public Cars(List<Car> cars) {
    	this.cars = new ArrayList<>(cars);
    }
    
    public List<Car> getCars() {
    		return Collections.unmodifiableList(cars);
    }
}

cars 를 새로운 값을 참조하도록 복사함으로써 외부에서 넘겨주는 List 와 내부적으로 사용하는 인스턴스 변수가 참조하는 값이 다르기 때문에 외부에서는 해당 값을 변경할 수 없다.

또한 getCars() 후 add 하는 걸 방지 하기 위해서 Collections.unmodifiableList 를 사용한다.

이러면 정말 외부에서 리스트를 담고 있는 변수인 cars 에 대해서 어떠한 변경도 하지 못하는 상태가 되었다.

장점

① Thread-safe하여 병렬 프로그래밍에 유용하며, 동기화를 고려하지 않아도 된다.
: 멀티 쓰레드에서 동기화 문제가 발생하는 이유가 공유 자원에 동시에 쓰기 때문인데, 공유 자원이 불변이라면 항상 동일한 값을 반환하기 때문에 동기화를 고려하지 않아도 된다.

② 실패 원자적인(Failure Atomic) 메소드를 만들 수 있다.
: 가변 객체로 작업을 하다가 예외가 발생하면 객체 상태를 보장할 수 없는데 불변 객체는 어떤 예외가 발생해도 메소드 호출 전의 상태를 유지할 수 있다.

③ Cache, Map, Set 등의 요소로 활용하기에 적합하다.
④ 부수 효과(Side Effect)를 피해 오류 가능성을 최소화할 수 있다.
⑤ 다른 사람이 작성한 함수를 예측 가능하며 안전하게 사용할 수 있다.

레퍼런스

[Java] 가변 객체 vs 불변 객체
불변 객체란? Java Immutable Object

0개의 댓글