본문 바로가기

STUDY/이펙티브자바

8-2) 방어적 복사본을 만들라

자바는 안전한 언어다

 

int nums1[4] = {1,2,3,4};
int nums2[4] = {5,6,7,8};

// 5 출력
std::cout << nums2[0];

*(nums1 + 4) = 0;

// 0 출력
std::cout << nums2[0];

자바는 안전한 언어이다. C, C++과 같은 언어는 높은 성능과 자유도를 얻은 대신 높은 메모리 관련 오류 가능성을 가지고 있다. 실제로 위의 코드를 보면 nums1, nums2 두개의 배열이 존재한다. 그런데 nums1을 통해서 nums2의 값을 바꾸는 것이 가능하다. 이는 C++ 변수들의 메모리 주소를 공개하고 조작할 수 있도록 허용했기 때문이다. 하지만 Java는 그렇지 않기에 하나의 변수로 다른 변수의 메모리를 조작하는것은 불가능하다.

 

 


Period: 불변 클래스 실패

 

public final class Period {
    private final Date start;
    private final Date end;
    
    public Period(Date start, Date end) {
        if (start.compareTo(end) > 0)
            throw new IllegalArgumentException(
                    start + " after " + end);
        this.start = start;
        this.end   = end;
    }

    public Date start() {
        return start;
    }
    public Date end() {
        return end;
    }
}

// Period에 사용된 Date 객체의 값이 바뀌었다.
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78);

불변 클래스란 생성되고 난 이후 값이 변하지 않는 불변 객체를 만드는 클래스를 말한다. 개발자는 Period를 불변 클래스로 만들고 싶었고, 이를 위해 모든 속성에 final을 사용하여 변할 수 없도록 하였다. 하지만 final 키워드때문에 start와 end를 다른 date 객체로 바꿀 수 는 없지만, start와 end에 사용된 기존 객체의 값은 바꿀 수 있다. 즉 불변성이 지켜지지 못한 것이다.

 

 


Period: 불변 클래스 성공

 

public final class Period {
    private final Date start;
    private final Date end;
    
    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 + " after " + this.end);
    }

    public Date start() {
        return new Date(start.getTime());
    }

    public Date end() {
        return new Date(end.getTime());
    }
}

// 이제 Period에 사용된 Date 객체의 값이 바뀌어도 영향이 없다.
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78);

생성자에 매개변수로 받은 객체들을 바로 사용하는 것이 아니라 new Date(start.getTime()) 처럼 새로운 복사본을 만들어서 사용하였다. 그리고 내부 속성을 반환할 때도 새로운 복사본을 만들어서 반환하도록 하였다. 이제 해당 객체는 생성자에 사용된 Date객체가 외부에서 어떻게 수정되더라도 불변성을 유지할 수 있다.

 

List<Integer> nums = new ArrayList<Integer>();
nums.add(1);
nums.add(2);
nums.add(3);
List<Integer> immuneList = Collections.unmodifiableList(nums);
immuneList.set(0, 9999);
nums.set(0, 9999);
728x90