
package me.whiteship.chapter08.item50;
import java.util.Date;
// 코드 50-1 기간을 표현하는 클래스 - 불변식을 지키지 못했다. (302-305쪽)
public final class Period {
private final Date start;
private final Date end;
/**
* @param start 시작 시각
* @param end 종료 시각. 시작 시각보다 뒤여야 한다.
* @throws IllegalArgumentException 시작 시각이 종료 시각보다 늦을 때 발생한다.
* @throws NullPointerException start나 end가 null이면 발생한다.
*/
public Period(Date start, Date end) {
if (start.compareTo(end) > 0)
throw new IllegalArgumentException(
start + "가 " + end + "보다 늦다.");
this.start = start;
this.end = end;
}
public Date start() {
return start;
}
public Date end() {
return end;
}
public String toString() {
return start + " - " + end;
}
// 코드 50-3 수정한 생성자 - 매개변수의 방어적 복사본을 만든다. (304쪽)
// public Period(Date start, Date end) {
// this.start = new Date(start.getTime());
// this.end = new Date(end.getTime());
//
// if (this.start.compareTo(this.end) > 0)
// throw new IllegalArgumentException(
// this.start + "가 " + this.end + "보다 늦다.");
// }
//
// // 코드 50-5 수정한 접근자 - 필드의 방어적 복사본을 반환한다. (305쪽)
// public Date start() {
// return new Date(start.getTime());
// }
//
// public Date end() {
// return new Date(end.getTime());
// }
// 나머지 코드 생략
}
package me.whiteship.chapter08.item50;
import java.util.Date;
// '불변'인 Period의 내부를 공격하는 두 가지 예 (303-305쪽)
public class Attacks {
public static void main(String[] args) {
// 코드 50-2 Period 인스턴스의 내부를 공격해보자. (303쪽)
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // p의 내부를 변경했다!
System.out.println(p);
// 코드 50-4 Period 인스턴스를 향한 두 번째 공격 (305쪽)
start = new Date();
end = new Date();
p = new Period(start, end);
p.end().setYear(78); // p의 내부를 변경했다!
System.out.println(p);
}
}
Date라는 객체가 변경이 가능한 객체다.
외부에서 값의 변경이 가능해진다.
package me.whiteship.chapter08.item50;
import java.util.Date;
// 코드 50-1 기간을 표현하는 클래스 - 불변식을 지키지 못했다. (302-305쪽)
public final class Period {
private final Date start;
private final Date end;
/**
* @param start 시작 시각
* @param end 종료 시각. 시작 시각보다 뒤여야 한다.
* @throws IllegalArgumentException 시작 시각이 종료 시각보다 늦을 때 발생한다.
* @throws NullPointerException start나 end가 null이면 발생한다.
*/
// public Period(Date start, Date end) {
// if (start.compareTo(end) > 0)
// throw new IllegalArgumentException(
// start + "가 " + end + "보다 늦다.");
// this.start = start;
// this.end = end;
// }
public Date start() {
return start;
}
public Date end() {
return end;
}
public String toString() {
return start + " - " + end;
}
// 코드 50-3 수정한 생성자 - 매개변수의 방어적 복사본을 만든다. (304쪽)
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(
this.start + "가 " + this.end + "보다 늦다.");
}
//
// // 코드 50-5 수정한 접근자 - 필드의 방어적 복사본을 반환한다. (305쪽)
// public Date start() {
// return new Date(start.getTime());
// }
//
// public Date end() {
// return new Date(end.getTime());
// }
// 나머지 코드 생략
}
방어적 복사를 한다.

package me.whiteship.chapter08.item50;
import java.util.Date;
// '불변'인 Period의 내부를 공격하는 두 가지 예 (303-305쪽)
public class Attacks {
public static void main(String[] args) {
// 코드 50-2 Period 인스턴스의 내부를 공격해보자. (303쪽)
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
// end.setYear(78); // p의 내부를 변경했다!
// System.out.println(p);
// 코드 50-4 Period 인스턴스를 향한 두 번째 공격 (305쪽)
start = new Date();
end = new Date();
p = new Period(start, end);
p.end().setYear(78); // p의 내부를 변경했다!
System.out.println(p);
}
}
setYear은 Period가 제공해 준 메서드가 아닌데 외부에서 바뀐게 period에 적용이 되어 버렸다.
package me.whiteship.chapter08.item50;
import java.util.Date;
// 코드 50-1 기간을 표현하는 클래스 - 불변식을 지키지 못했다. (302-305쪽)
public final class Period {
private final Date start;
private final Date end;
/**
* @param start 시작 시각
* @param end 종료 시각. 시작 시각보다 뒤여야 한다.
* @throws IllegalArgumentException 시작 시각이 종료 시각보다 늦을 때 발생한다.
* @throws NullPointerException start나 end가 null이면 발생한다.
*/
// public Period(Date start, Date end) {
// if (start.compareTo(end) > 0)
// throw new IllegalArgumentException(
// start + "가 " + end + "보다 늦다.");
// this.start = start;
// this.end = end;
// }
// public Date start() {
// return start;
// }
// public Date end() {
// return end;
// }
public String toString() {
return start + " - " + end;
}
// 코드 50-3 수정한 생성자 - 매개변수의 방어적 복사본을 만든다. (304쪽)
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(
this.start + "가 " + this.end + "보다 늦다.");
}
//
// // 코드 50-5 수정한 접근자 - 필드의 방어적 복사본을 반환한다. (305쪽)
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime());
}
// 나머지 코드 생략
}
이렇게 new Date로 해서 넘겨준다.

이렇게 하면 둘다 78년으로 안바뀌고 현재 시간으로 그대로 준다.
방어적 복사 안해도 되는 경우