방어적 복사

외부에서 전달받은 객체나 내부 객체를 반환할 때, 원본이 예기치 않게 변경되는 것을 막기 위해 복사본을 만들어 사용하는 기법입니다.

왜 필요한가?

public class Period {
    private final Date start;
    private final Date end;

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

    public Date getStart() { return start; }
}

// 외부에서 원본 변경 가능
Date start = new Date();
Period period = new Period(start, new Date());
start.setTime(0L);  // period 내부 start도 변경됨!

방어적 복사 적용

public class Period {
    private final Date start;
    private final Date end;

    // 생성자 - 입력값 복사
    public Period(Date start, Date end) {
        this.start = new Date(start.getTime());  // 방어적 복사 O
        this.end = new Date(end.getTime());
    }

    // getter - 반환값 복사
    public Date getStart() {
        return new Date(start.getTime());  // 방어적 복사 O
    }
}

// 이제 외부에서 변경 불가
Date start = new Date();
Period period = new Period(start, new Date());
start.setTime(0L);  // period 내부엔 영향 없음 O

적용 시점

시점설명
생성자 입력외부 객체가 내부로 들어올 때 복사
getter 반환내부 객체가 외부로 나갈 때 복사

얕은 복사 vs 깊은 복사

얕은 복사깊은 복사
복사 범위1단계만 복사중첩 객체까지 모두 복사
방어적 복사단순 객체에 적합중첩 객체에 필요
// 얕은 복사 - 내부 객체는 여전히 공유
List<int[]> copy = new ArrayList<>(original);

// 깊은 복사 - 내부까지 모두 복사 O
List<int[]> deepCopy = original.stream()
    .map(arr -> arr.clone())
    .collect(Collectors.toList());

방어적 복사 vs 불변 객체

방어적 복사불변 객체
방법복사본 생성아예 변경 불가
비용복사 비용 발생없음
권장가변 객체 사용 시가능하면 우선 고려
// 불변 객체 사용 시 방어적 복사 불필요
// LocalDateTime은 불변 객체
public class Period {
    private final LocalDateTime start;  // 방어적 복사 필요 없음 O
}

가장 좋은 방법은 불변 객체(Immutable Object)를 사용하는 것이며, 가변 객체를 써야 할 때 방어적 복사로 보완합니다.

CSLOG에서 보기