자바 API 로는 깊은 복사를 수행할 수 없다는 것을 알게 됐다. 따라서 외부 혹은 내부로부터 변경에 취약하지 않도록 내부 요소들이 불변 객체여야 한다.
방어적 복사는 생성자를 통해 초기화하거나 내부의 객체를 반환할 때, 새로운 객체로 감싸서 복사해주는 방법이다. 외부와 내부에서 주소 값을 공유하는 인스턴스의 관계를 끊어주기 위해서 방어적 복사를 사용한다.
또한, 외부로 값을 노출시킬 때도 관계를 끊어주기 위해서 복사본을 반환해준다.
1. 주소 복사
List<Number> copyNumbers = numbers;
=의 경우 그저 참조변수를 하나 만들어 주소를 넣어주기 때문에 완전히 동일한 컬렉션이다.
2. = new ArrayList<>()
List<Number> newNubmers = new ArrayList<>(numbers);
방어적 복사를 해줌으로써 컬렉션의 주소값이 달라져 원본 컬렉션의 변경에 영향을 받지 않게 된다.
하지만 중요한 점은 요소의 주소값은 동일하기 때문에 요소의 변경에는 영향을 받게 된다.
3. addAll()
copyNumbers.addAll(numbers);
new ArrayList<>()와 동일하기 때문에 컬렉션의 주소는 달라지지만 요소의 주소는 같다.
주로 복사 보단 List끼리 합쳐주기주기 위해서 사용된다.
그렇기 때문에 인텔리제이에선 친절히 대체할 수 있다고 알려준다.
4. stream().collect(Collectiors.toList())
List<Number> copyNumbers = numbers.stream().collect(Collectors.toList());
이 또한 new ArrayList<>()와 동일하기 때문에 컬렉션의 주소는 달라지지만 요소의 주소는 같다.
마찬가지로 인텔리제이에서 알려주게 된다.
5. Collections.unmodifiableList()
List<Number> copyNumbers = Collections.unmodifiableList(numbers);
new ArrayList<>()와 동일하기 때문에 컬렉션의 주소는 달라지지만 요소의 주소는 같다.
컬렉션을 변경하려고 하면 예외가 발생하여 막혀있지만 원본 컬렉션의 변경이 일어나면 복사한 컬렉션에도 그대로 적용이 되기 때문이다.
6. List.copyOf()
List<Number> copyNumbers = List.copyOf(numbers);
List.copyOf()는 unmodifiableList와 비슷하지만 원본 컬렉션의 변경에도 영향을 받지 않는다.
즉, 컬렉션의 주소는 다르고 요소의 주소는 같으며 복사된 컬렉션에 대한 변경 시도시 UnsupportedOperationException 발생하며 원본 컬렉션의 변경에도 영향을 받지 않는다는 것이다.
그럼 copyOf()만 사용하면 완전한 불변이겠네.
No! 요소의 주소는 같다. 이는 결국 요소의 변경이 일어나면 모두 영향을 받게 된다는 것이다.
그렇기 때문에 불변 객체, VO가 중요하다는 것이다.
만약 컬렉션의 요소가 불변임을 보장할 수 있게 된다면 copyOf를 통해 완전한 불변이 가능.
컬렉션의 복사 방법을 정리해봅시다! (unmodifiable view / list)
방어적 복사, unmodifiable, 불변... 너무 어려워서 정리해봅니다! 생성자의 파라미터로 리스트를 받는다면❓ 루피로부터 생성자 내부 검증 및 할당 과정에 대해 피드백을 받았습니다! 실은 피드백
creampuffy.tistory.com
Unmodifiable Collection? Collection.copyOf?
이 글은 전반적으로 컬렉션의 복사 방법을 정리해봅시다! (unmodifiable view / list) 를 참고하였습니다. 더 자세한 내용을 원하시면 리차드님 블로그를 방문해주세요! 좋은 글이 넘쳐납니다!! 뭐가 다
studyhardd.tistory.com