[Java] 불변객체(immutable object)

Junseo Kim·2021년 2월 10일
0

[Java]자바 기초

목록 보기
28/35

불변객체란?

생성 후에 내부의 값을 바꿀 수 없는 객체를 말한다.

자바에서는 대표적인 불변객체로 String을 예로 들 수 있다.

String string = "abc";
string = "def";

위의 코드는 string이라는 변수의 값이 바뀐 것 처럼 보이지만, 실제로는 "def"라는 새로운 객체가 생성되고 string변수가 "def"를 가리키게 한 것이다. 즉 원래 "abc"객체의 값은 여전히 "abc"로 남아있다. 더 이상 참조되지 않는 것 뿐이다.

즉 불변객체는 new 인스턴스로 재할당하는 것 이외에는 값을 바꿀 수 없다.

원시타입을 멤버변수로 가지고 있는 객체를 불변객체화 해보자.

아래와 같이 변수에 final을 붙여주면 Racer는 불변객체가 된다. 외부에서 값을 바꿔줄 수 없기 때문이다.

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

만약 아래와 같은 테스트는 어떻게 될까?

class ApplicationTest {

    @Test
    void equals() {
        Racer racer1 = new Racer("air");
        assertEquals(racer1, new Racer("air"));
    }
}

racer1new Racer("air")는 둘다 같은 이름을 가졌지만 테스트는 실패한다. 가지고 있는 주소값이 서로 다르기 때문이다.

따라서 불변객체를 사용할때는 거의 필수적으로 equalshashcode를 override 해줘야한다.

public class Racer {
    private final String name;
	
    public Racer(String name) {
    	this.name = name;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Racer racer = (Racer) o;
        return Objects.equals(name, racer.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

이후 다시 테스트를 진행하면 테스트가 통과한다.

참조타입의 불변객체

만약 참조타입인 경우는 어떻게 될까?

일반 객체

public class Car {
    private final Racer racer;
}

class Racer {
    private final String name; // final이 없다면 Car는 불변객체가 될 수 없다.
    
    public void setName(String name) {
    	this.name = name;
    }
}

이 경우는 Racer에 만약 final이 붙어있지 않다면 Car는 불변객체가 될 수 없다. Car 자체는 final일지라도 Racer의 값은 setter가 있다면 변경될 수 있기 때문이다. 따라서 이런 경우 모두 final을 붙여줘야한다.

List

list인 경우 아래와 같이 새로 감싸주지않는다면 주소값이 같기때문에 데이터의 변경이 반영되므로 불변객체라고 할 수 없다. 따라서 아래와 같이 new ArrayList<>()로 감싸주면 내부의 cars는 새로운 주소가 되기 때문에 외부와 연결이 끊어진다.

외부에서 유입되는 데이터 뿐만아니라 외부로 데이터를 내보낼 때도 새로 감싸서 내보낸다. getCars()의 Collections.unmodifiableList도 새로운 주소값을 가진 List를 반환해준다.

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);
    }
}

불변객체의 장단점

이런 불변객체를 사용하는 이유는 객체에 대한 신뢰성이 높아진다. 한 번 생성되고 값이 변하지 않기 때문이다.

하지만 매번 새로운 인스턴스를 생성해야해서 성능이 떨어질 수 있다고 생각할 수 있다. 그러나 불변객체를 사용함으로서 얻는 이점이 메모리 걱정보다 더 크다고 오라클문서에 나와있다고 한다.(메모리는 GC에 맡긴다..)

0개의 댓글