[JAVA] 불변 클래스
0. 이 글을 쓰게 된 이유
개발을 할 때 수도 없이 클래스는 웬만하면 불변 클래스로 만들라는 소리를 들었다. 나름의 이유들을 들었는데 명확하게 공감되지 않았다. 도대체 왜 불변 클래스로 만들어야하는가에 대한 명확한 이유를 알기위해 이 글을 쓰게 되었다.
1. 불변 클래스란?
이펙티브 자바에서 불변 클래스란 그 인스턴스 내부 값을 수정할 수 없는 클래스라고 만한다. 즉, 해당 인스턴스가 가지고있는 정보들을 수정할 수 없도록 만들어 변하지 않게 하는 클래스이다.
2. 왜 불변 클래스를 사용해야할까?
불변 클래스는 가변 클래스보다 설계하고 구현하고 사용하기 쉽고, 오류가 생길 여지도 적고 훨씬 안전하다고 한다. 왜 그럴까?
2.1 불변 클래스는 그 자체로 실패 원자성을 제공한다.
실패 원자성이란 메서드에서 예외가 발생한 후에도 그 객체는 여전히 유효한 상태여야한다는 성질이다. 불변 객체는 내부 상태가 변하지 않으니 그 자체로 이 성질을 만족한다.
2.2 불변 클래스는 스레드 안전하다.
여러 스레드가 가변 객체의 한 필드로 접근해서 수정한다고 가정하자. 따로 락을 걸어놓지 않으면 그 필드의 값은 여기저기에서 동시에 수정되어 정확하지 않은 값이 되게 된다.
하지만 불변 객체에서는 그럴 일이 없다. 여러 스레드에서 접근하여도 그 값은 조회 정도만 되기 때문에 애초에 수정될 일이 없다. 즉, 스레드끼리 서로 영향을 주지 않는다. 그러므로 Thread safe 하다고 볼 수 있다.
2.3 객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다.
불변 객체인 구성요소들로 이뤄진 객체라면 그 구조가 아무리 복잡하더라도 불변식을 유지하기 훨씬 쉽다.
단적인 예로, Map이나 Set의 원소로 쓰기에 적절하다. Map이나 Set은 안에 담긴 값이 바뀌면 불변식이 허물어지는데, 불변 객체를 사용하면 그런 걱정은 하지않아도 된다.
정리하자면, 불변 클래스를 만들게 되면 해당 인스턴스의 정보은 Read-Only가 되어 여러 문제들의 가능성 자체를 막는다.
클래스는 꼭 필요한 경우가 아니라면 불변이어야한다. 만약 불변으로 만들 수 없는 클래스라면 변경할 수 있는 부분을 최소한으로 줄이자.
3. 불변 객체를 만드는 법
객체의 상태를 변경하는 메서드(변경자)를 제공하지 않는다.
Setter외에도 객체의 상태를 변경하는 모든 메소드가 포함된다.
클래스를 확장할 수 없도록 한다.
하위 클래스에서 부주의하게 혹은 나쁜 의도로 객체의 상태를 변하게 만드는 사태를 막아준다. 상속을 막는 대표적인 방법은 클래스를 final로 선언하는 것이다. 이외에도 생성자를 막고 정적 팩토리 메소드만을 제공하는 방법이 있다.
모든 필드를 final로 선언한다.
모든 필드의 변경 가능성을 없애 불변하게 만드는 것이다. 새로 생성된 인스턴스를 동기화 없이 다른 스레드로 건네도 문제없이 동작하게끔 보장한다.
모든 필드를 private으로 선언한다.
클라이언트에서 직접 접근해 수정하는 일을 막아준다.
자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
클래스에 가변 객체를 참조하는 필드가 하나라도 있다면 클라이언트에서 그 객체의 참조를 얻을 수 없도록 제한해야한다. 그 가변 객체가 수정이되면 해당 클래스의 상태도 변하는 꼴이기 때문이다. 대표적인 방법으로 방어적 복사가 있다.