
불변성이 강조되는 객체지향 프로그래밍의 특성상 자바에서도 UnmodifiableList라는 클래스가 존재한다

해당 UnmodifiableList 선언하는 방식이다.
List<Integer> unmodifiableList = Collections.unmodifiableList(list);
클래스 내부를 보니 단순히 리스트만 가지고 생성자는 이 리스트에 인자로 받은 리스트만 대입할 뿐이다. 그래서 불변이 보정되지 않는다.

UnmodifiableList의 구조를 그림으로 보면 다음과 같다.

여기서 Unmodifiable 객체의 메소드 중 변경과 관련된 메소드를 호출하면 다음과 같이 동작한다.

얼핏 보면 "변경과 관련된 메소드는 모두 예외를 터뜨리니까 불변성이 보장된게 아닌가?" 싶은 생각이 든다. 하지만 생성자의 this.list = list에서 볼 수 있듯이, UnmodifiableList 내부의 리스트는 인자로 받은 원본 리스트와 완전히 동일한 객체이다.(그림에서도 두 리스트가 연결된 것으로 표현했다.)
원본 리스트의 변경은 막을 수 없다!
아래 그림처럼 원본 리스트에 변경을 시도하면 어떻게 될까?

애석하게도 UnmodifiableList라는 이름이 무색하게 내부의 리스트까지 변경되어 버린다. 내부의 리스트와 원본 리스트가 동일한 객체를 바라보고 있기 때문이다.
물론 대책이 없지는 않다. 원본 리스트가 UnmodifiableList의 내부 리스트와 동일한 객체를 바라보는 것이 문제이므로, 새로운 리스트를 복제해서 사용하면 된다.
Java 8 이상
List<Integer> unmodifiableList = Collections.unmodifiableList(new ArrayList<>(list));
Java 10 이상
List<Integer> unmodifiableList = List.copyOf(list);
List<Integer> unmodifiableList = list.stream()
// ...
.collect(Collectors.toUnmodifiableList());
// 16이상
List<Integer> unmodifiableList = list.stream()
// ...
.toList();