[Effective Java 3/e] 아이템 17 - 변경 가능성을 최소화하라
아이템 17 - 변경 가능성을 최소화하라
자바의 대표적인 불변 클래스
- String, BigInteger, BigDecimal
클래스를 불변으로 만들기 위한 다섯 가지 규칙
- 객체의 상태를 변경하는 메서드(변경자)를 제공하지 않는다.
- 클래스를 확장할 수 없도록 한다.
- 상속을 막는 대표적인 방법 - final로 선언
- 모든 필드를 final로 선언한다.
- 모든 필드를 private로 선언한다.
- 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
불변 복소수 클래스
public class Complex {
private final double re;
private final double im;
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);
}
}
- plus 메서드는 자신의 인스턴스를 수정하지 않고 새로운 Complex 인스턴스를 만들어 반환한다.
불변 객체의 장점
- 불변 객체는 단순하다.
- 불변 객체는 근본적으로 스레드 안전하여 따로 동기화할 필요 없다.
- 불변 객체는 안심하고 공유할 수 있다.
- 불변 클래스라면 한번 만든 인스턴스를 최대한 재활용하기를 권장
- 가장 쉬운 재활용 방법은 상수로 제공
- public static final Complex ZERO = new Complex(0, 0);
- 불변 객체끼리는 내부 데이터를 공유할 수 있다.
- 불변 객체들을 구성요소로 사용하면 이점이 많다.
- 불변 객체는 맵의 키와 집합(Set)의 원소로 쓰기 좋다.
- 불변 객체는 그 자체로 실패 원자성을 제공한다.
- 실패 원자성(failure atomicity) - 메서드에서 예외가 발생한 후에도 그 객체는 여전히(메서드 호출 전과 똑같은) 유효한 성질이어야 한다.
불변 객체의 단점
- 값이 다르면 반드시 독립된 객체로 만들어야 한다.
- 값의 가지수가 많다면 큰 비용이 든다.
- 해당 문제를 대체 하는 방법
- 흔히 쓰일 다단계 연산들을 예측하여 기본 기능으로 제공하는 방법
- 다단계 연산을 기본으로 제공하면 더 이상 각 단계마다 객체를 생성하지 않아도 된다.
- 흔히 쓰일 다단계 연산들을 예측하여 기본 기능으로 제공하는 방법
생성자 대신 정적 팩터리를 사용한 불변 클래스
public class Complex {
private final double re;
private final double im;
private Complex(double re, double im) {
this.re = re;
this.im = im;
}
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
}
- 패키지 바깥의 클라이언트에서 바라본 이 불변 객체는 사실상 final
- 정적 팩터리 방식은 다수의 구현 클래스를 활요한 유연성 제공, 객체 캐싱 기능을 추가해 성능을 끌어올릴 수도 있다.
정리
- 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.
- 불변으로 만들 수 없는 클래스라도 변경할수 있는 부분을 최소한으로 줄이자.
- 다른 합당한 이유가 없다면 모든 필드는 privte final 이어야 한다.
- 생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.
- 위의 규칙을 잘 지킨 클래스 - java.util.concurrent.CountDownLatch 클래스