인스턴스의 내부 값을 수정할 수 없는 클래스
- 가변 클래스보다 설계하고 구현하고 사용하기 쉽다.
- 오류가 생길 여지가 적고 훨씬 안전하다.
Setter
... 등등 알죠?
상속이 가능하면 하위 객체는 오버라이딩
등 불변을 보장할 수 없게 된다.
상속하지 못하게 하는 방법
모든 생성자를 `private` 혹은 `package-private`으로 만들고, `public 정적 팩토리`를 제공한다. private Yield(float yield) {
this.yield = yield;
}
public static Yield calculate(LottoMoney lottoMoney, Long totalWinningMoney) {
return new Yield(lottoMoney.divide(totalWinningMoney));
}
설계자의 의도를 명확하게 드러내고 멀티 쓰레드
에서도 안전할 수 있게 한다.
필드가 참조하는 가변 객체를 클라이언트에서 직접 접근해 수정하는 일을 막아준다.
가변 컴포넌트(배열, Collection 등)
를 가지는 필드가 하나라도 있다면 클라이언트에서 참조를 얻을 수 없도록 해야한다. 방어적 복사
를 수행하라.
피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴
class Car {
private final int position;
Car(int position) {
this.position = position;
}
Car move() {
return new Car(position + 1);
}
}
##불변 객체
의 장점👍은?
생성 시점의 상태를 파괴될 때까지 간직하므로 믿고 사용할 수 있다.
어떤 스레드도 다른 스레드에 영향을 줄 수 없으므로 안심하고 공유할 수 있다.
- 여러 클라이언트가 인스턴스를 공유하여 메모리 사용량과 GC 비용이 줄어든다.
- 방어적 복사도 필요 없으므로
clone
메서드나복사 생성자
를 제공하지 않는 게 좋다.
public class BigInteger extends Number implements Comparable<BigInteger> {
final int signum;
final int[] mag;
public BigInteger negate() {
return new BigInteger(this.mag, -this.signum);
}
mag
배열은 비록 변이지만 복사하지 않고 원본 인스턴스와 공유해도 된다. 그 결과 새로 만든 BigInteger
인스턴스도 원본 인스턴스가 가리키는 내부 배열을 그대로 가리킨다.
불변 객체로 이뤄진 객체라면 아무리 복잡해도 불변식을 유지하기 훨씬 쉽다. 불변 객체는 맵의 키
와 집합(Set)의 원소
로 쓰기에 좋다.
public class LottoResult {
private final Map<Rank, Long> result;
실패 원자성(failure atomicity)
이란 메서드에서 예외가 발생한 후에도 그 객체는 여전히 호출 전과 같은 유효한 상태여야 한다는 성질이다. 불변 객체의 메서드는 내부 상태를 바꾸지 않으니 이 성질을 만족한다.
객체 생성 비용이 높고 상태가 다른 객체를 자주 만들어야 하는 객체라면 큰 비용이 들 수 있다.
다단계 연산
을 제공하여 각 단계마다 객체를 생성하지 않도록 한다.StringBuilder
-
변경할 수 있는 부분을 최소한으로 줄인다.
나머지 모두를
final
로 선언한다. 다른 합당한 이유가 없다면 모든 필드는private final
이어야 한다. -
생성자
는 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.