불변 클래스란 간단히 말해 그 인스턴스의 내부 값을 수정 할 수 없는 클래스다. 객체가 파괴되는 순간까지 절대 달라지지 않는다. 클래스를 불변으로 만드려면 다음 다섯 가지 규칙을 따르면 된다.
불변 복소수 클래스의 예를 확인 해보자.
위에 구현된 사칙연산 메서드들은 인스턴스 자신은 수정하지 않고, 새로운 Complex 인스턴스를 만들어 반환하는 모습에 주목하자. 피연산자에 함수를 적용해 결과를 반환하지만 피연산자 자체는 그대로인 프로그래밍 패턴은 함수형 프로그래밍이라고 한다.
불변객체는 근본적으로 스레드 안전해서 따로 동기화할 필요가 없다. 한번 만든 불변 클래스는 최대한 재활용 하는것이 좋다. 가장 쉬운 재활용 방법은 상수로 제공하는 것이다.
불변 객체는 자유롭게 공유 할 수 있음은 물론, 불변 객체끼리는 내부 데이터를 공유 할 수 있다. 객체를 만들때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다. 값이 바뀌지 않는다면 그 구조가 아무리 복잡하더라도 불변식을 유지하기 훨씬 수월하기 때문이다.
불변 객체는 그 자체로 실패 원자성을 제공한다. 실페 원자성이랑 메서드에서 예외가 발생하더라도 그 객체는 여전히 호출전과 같은 상태라는 것이다.
불변 객체에도 단점이 있다. 값이 다르면 반드시 독립된 객체로 만들어야 한다는 것이다. 예를들어 백만 비트짜리 불변 객체가 있는데 하나의 비트값만 바꿔야 한다면 백만 비트짜리 객체를 하나 더 만들어야 한다.
이 문제에 대처하는 방법으로 흔히 쓰일 다단계 연산들을 예측하여 기본 기능으로 제공하는 방법이다. 다단계 연산을 기본으로 제공하면 더 이상 각 단계마다 객체를 생성하지 않아도 된다.
불변 클래스는 만드는 또다른 방법으로는 모든 생성자를 private 혹은 package-private으로 만들고 public 정적 패터리르 제공하는 방법이다.
패키지 바깥의 클라이언트에서 바라본 이 불변 객체는 사실상 final이다. 정적 팩터리 방식은 다수의 구현 클래스를 활요한 유연성 제공, 객체 캐싱 기능을 추가해 성능을 끌어올릴 수도 있다.
클래스는 필요한 경우가 아니라면 불변이어야 한다. 불변 클래스는 장점이 많으며, 단점이라곤 특정 상황에서의 잠재적 성능 저하 뿐이다. 하지만 모든 클래스를 불변으로 만들 수는 없다. 불변으로 만들 수 없는 클래스라도 변경 할 수 있는 부분을 최소한으로 줄이자. 객체가 가질 수 있는 상태의 수를 줄이면 객체를 예측하기 쉽고 오류도 줄어든다.