Item 39. 필요하다면 방어적 복사본을 만들라.

자바는 다른언어(C, C++등과 같이)처럼 네이티브 함수(native method)를 사용하지 않는 언어 이기 때문에 안전한 언어(safe language)이다. 버퍼 오버런(buffer overrun, buffer overflow)이나 배열 오버런(array overrun), 와일드 포인트(wild pointer, 잘못된 객체 참조) 같은 메모리 훼손 오류(memory corruption error)가 생기지 않는다.

하지만, 내가 자바로 만드는 클래스를 사용하는 클라이언트가 불변식(invariant)을 망가뜨리기 위해 최선을 다할 것이라는 가정하에, 방어적으로 프로그래밍 해야한다.


public class Period{
    /**
     * 불변 객체를 만들기 위해서, final로 선언한다고 해서 불변 객체가 되는 것은 아니다. 
     * 참조하는 객체(Date)객체가 불변성을 지원해야 가능하다. 
     * 참조하는 객체가 불변성을 지원하지 않는 다면, "방어적 복사"를 통해 객체 안정성을 확보하자.
     */
    private final Date start;
    private final Date end;
    
    
    public Period(Date dtart, 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 + " after " + this.end);
    }
    // 수정된 접근자 - 내부 필드의 방어적 복사본 생성
    public Date start() {
        return new Date(start.getTime());
    }
    
    public Date end() {
        return new Date(end.getTime());
    }
}


// use
public static void main(String[] args){
    Date start = new Date();
    Date end = new Date();
    
    Period p = new Period(start, end);
    
    /**
     * end 객체에 대해서, p 객체가 래퍼런스 하고 있는 것처럼 보이지만
     * Period 생성자에서 "안전한 복사"를 하고 있기 때문에
     * end 객체의 year가 변경되어도 P객체에는 영향이 없음
     */
    end.setYear(78);  
}

인자로 전달된 객체의 자료형이 계승이 가능하다면(final 클래스가 아니라면) 방어적 복사본을 만들 때 clone을 사용하지 않도록 해야 한다.

방어적 복사가 필요할 것으로 예상되는 클래스

결론

클라이언트에게 받는 인자나 반환하는 객체가 변경가능한 객체라면 방어적 복사를 사용하여, 사용하는 객체의 안전성을 보장 할수 있도록 노력하자. 만약 방어적 복사를 하기가 힘들다면(복사 비용이 크거나(큰 리스트 객체와 같은), 변경이 빈번한 객체와 같은 경우) 문서로 명시해서 참조 되는 객체의 상태가 변경하지 않도록 가이드 하자.