[Effective Java 3/e] 아이템 50 - 적시에 방어적 복사본을 만들라


아이템 50 - 적시에 방어적 복사본을 만들라

기간을 표현하는 클래스 - 불변식을 지키지 못했다

public final class Period {
  private final Date start;

  public Period(Date start) {
    this.start = start;
  }

  public Date start() {
    return start;
  }
}

Period 인스턴스의 내부 공격

Date start = new Date();
Period p = new Period(start);
start.setYear(78);  // p의 내부를 수정했다
  • Date가 가변이라서 발생 한 문제
    • 자바 8 이후로는 Date 대신 불변인 Instant를 사용하면 된다.(LocalDateTime이나 ZonedDateTime 사용 가능)
    • Date는 낡은 API이니 새로운 코드 작성시 더 이상 사용하면 안된다.
  • 생성자에서 받은 가변 매개변수 각각을 방어적으로 복사(defensive copy)해야 한다.

수정한 생성자 - 매개변수의 방어적 복사본을 만든다

public Period(Date start) {
  this.start = new Date(start.getTime());
}
  • 방어적 복사에 Date의 clone 메서드를 사용하지 않은 점에 주목
    • Date는 final이 아니므로 clone이 Date가 정의한게 아닐 수 있다.
    • 매개변수가 제3자에 의해 확장될 수 있는 타입이라면 방어적 복사본을 만들 때 clone을 사용해서는 안 된다.

Period 인스턴스를 향한 두 번째 공격

Date start = new Date();
Period p = new Period(start);
p.start.setYear(78); // p의 내부를 변경했다.

수정한 접근자 - 필드의 방어적 복사본을 반환한다

public Date start() {
  return new Date(start.getTime());
}
  • 생성자와 달리 접근자 메서드에서는 방어적 복사에 clone을 사용해도 된다.
    • Period가 가지고 있는 Date 객체는 java.util.Date임이 확실하기 때문
    • 그래도 인스턴슬르 복사하는 데는 일반적으로 생성자나 정적 팩터리를 쓰는게 좋다.