불변 클래스는 인스턴스 내부 값을 수정할 수 없는 클래스다. String, 기본 타입의 박싱된 클래스들 등이 불변 클래스의 예다.
아래는 불변 클래스를 만드는데 필요한 규칙이다.
public final
로만 선언해도 불변 객체가 되지만, 이렇게 하면 다음 릴리스에서 내부 표현을 바꾸지 못하므로 비추.// 코드 17-1 불변 복소수 클래스 (106-107쪽)
public final class Complex {
private final double re;
private final double im;
public static final Complex ZERO = new Complex(0, 0);
public static final Complex ONE = new Complex(1, 0);
public static final Complex I = new Complex(0, 1);
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public double realPart() { return re; }
public double imaginaryPart() { return im; }
public Complex plus(Complex c) {
return new Complex(re + c.re, im + c.im);
}
// 코드 17-2 정적 팩터리(private 생성자와 함께 사용해야 한다.) (110-111쪽)
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
public Complex minus(Complex c) {
return new Complex(re - c.re, im - c.im);
}
public Complex times(Complex c) {
return new Complex(re * c.re - im * c.im,
re * c.im + im * c.re);
}
public Complex dividedBy(Complex c) {
double tmp = c.re * c.re + c.im * c.im;
return new Complex((re * c.re + im * c.im) / tmp,
(im * c.re - re * c.im) / tmp);
}
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Complex))
return false;
Complex c = (Complex) o;
// == 대신 compare를 사용하는 이유는 63쪽을 확인하라.
return Double.compare(c.re, re) == 0
&& Double.compare(c.im, im) == 0;
}
@Override public int hashCode() {
return 31 * Double.hashCode(re) + Double.hashCode(im);
}
@Override public String toString() {
return "(" + re + " + " + im + "i)";
}
}
이 사칙연산 메서드들은 인스턴스 자신은 수정하지 않고 새로운 Complex 인스턴스를 만들어 반환하고 있다. 이처럼 피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴을 함수형 프로그래밍이라 한다.
불변 객체는 생성된 시점의 상태를 파괴될 때까지 그대로 간직한다. 불변 객체는 근본적으로 스레드 안전하여 따로 동기화할 필요 없다.. 여러 스레드가 동시에 상요해도 훼손되지 않는다. 그러므로 불변 객체는 안심하고 공유할 수 있다.
불변 객체는 아무리 복사해봐야 원본과 똑같고 그러므로 방어적 복사도 필요없다. 그래서 clone 메서드나 복사 생성자를 제공하지 않는게 좋다.
불변 객체는 자유롭게 공유할 수 있음은 물론, 불변 객체끼리는 내부 데이터를 공유할 수 있다.
객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다. 구조가 복잡하더라도 불변식을 유지하기 수월하다.
불변 객체는 그 자체로 실패 원자성을 제공한다.(아이템 76). 즉 예외가 발생후에도 그 객체는 여전히 유효한 상태다.
값이 다르면 반드시 독립된 객체로 만들어야 한다. 값의 가짓수가 많다면 이들을 모두 만드는데 큰 비용이 필요하다.
클래스가 불변이라면 자신을 상속하지 못하게 해야 하는데 두 가지 방법이 있다.
package-private
으로 만들고 public
정적 팩터리 메서드 제공.물론 모든 클래스를 불변으로 만들 수 없으나, 불변으로 만들 수 없는 클래스도 변경 가능한 부분은 최소화하자. 다른 합당한 이유가 없다면 모든 필드는 private final
이어야 한다.
생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 반환해야 한다.