배열을 불변하게 만드는 방법
- 자바의 배열은 객체이며, 참조 타입(reference type)으로, 참조가 공유되면 외부에서 내용이 변경될 수 있습니다.
- 이러한 특성으로, 배열을 불변으로 만들기 위해서는 외부에서 값이 변경되지 않도록 배열을 복사하여 저장하고 반환해야 합니다. (방어적 복사)
import java.util.Arrays;
class ImmutableArray {
private final int[] array;
public ImmutableArray(int[] array) {
// 원본 배열을 복사하여 불변성을 유지
this.array = Arrays.copyOf(array, array.length);
}
public int[] getArray() {
// 배열의 복사본을 반환하여 외부 수정 방지
return Arrays.copyOf(array, array.length);
}
public int getElement(int index) {
return array[index]; // 특정 값만 반환
}
}
public class Main {
public static void main(String[] args) {
int[] nums = {1, 2, 3};
ImmutableArray immutableArray = new ImmutableArray(nums);
int[] arrayCopy = immutableArray.getArray();
System.out.println(Arrays.toString(arrayCopy)); // [1, 2, 3]
arrayCopy[0] = 99; // 복사본 수정
System.out.println(Arrays.toString(immutableArray.getArray())); // [1, 2, 3] (원본은 불변)
}
}
리스트를 불변하게 만드는 방법
- 리스트도 배열과 같이 참조 타입으로, 외부에서 내용이 변경될 수 있습니다.
- 가장 간단한 방법은 List.of()를 통해 불변 객체를 생성하는 것입니다. (Java 9 이상)
- List.of()로 만든 객체는 읽기는 자유롭게 가능하지만, ADD, REMOVE, SET 요청은 전부 UnsupportedOperationException을 던집니다.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> immutableList = List.of("Apple", "Banana", "Cherry");
System.out.println(immutableList); // [Apple, Banana, Cherry]
// immutableList.add("Date"); // UnsupportedOperationException 발생
}
}
- 또는 Collections.unmodifiableList(new ArrayList<>(list)); 방법으로도 새로운 리스트를 복제해서 사용할 수도 있습니다. (Java 8 이하)
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> originalList = new ArrayList<>();
originalList.add("Alpha");
originalList.add("Beta");
// 복제본을 사용한 안전한 방식
List<String> safeUnmodifiableList = Collections.unmodifiableList(new ArrayList<>(originalList));
System.out.println("Before modification:");
System.out.println("originalList: " + originalList);
System.out.println("safeUnmodifiableList: " + safeUnmodifiableList);
// 원본 리스트에 요소 추가
originalList.add("Gamma");
System.out.println("\nAfter modification:");
System.out.println("originalList: " + originalList);
System.out.println("safeUnmodifiableList: " + safeUnmodifiableList); // 그대로 유지
}
}
Before modification:
originalList: [Alpha, Beta]
safeUnmodifiableList: [Alpha, Beta]
After modification:
originalList: [Alpha, Beta, Gamma]
safeUnmodifiableList: [Alpha, Beta]
- 다만, Collections.unmodifiableList(list)로는 진정한 불변 객체를 생성할 수 없습니다.
- 이 메서드는 리스트를 읽기 전용으로 감싸 구조 변경을 막지만, 내부적으로는 원본 리스트를 그대로 참조합니다.
- add(), remove(), set() 등의 변경 메서드를 호출하면 UnsupportedOperationException 예외가 발생합니다.
List<String> list = new ArrayList<>();
List<String> unmodifiable = Collections.unmodifiableList(list);
unmodifiable.add("A"); // UnsupportedOperationException 발생
- 실제 unmodifiableList()의 구현을 보면, 리스트를 감쌀 때 복제를 수행하지 않고 원본 리스트를 직접 참조하는 것을 확인할 수 있습니다.
- 따라서 원본 리스트를 불변 리스트로 만들어야 하는 경우에는, 데이터를 복제한 후 unmodifiableList()로 감싸는 방식을 사용해야 진정한 불변성을 보장할 수 있습니다.
불변 객체의 장점
- 불변 객체는 한 번 생성되면 그 상태가 절대 변경되지 않기 때문에, 안정성이 높고 어떤 값이 들어 있을지 예측하기 수월합니다.
이는 side effect를 피할 수 있으며, 디버깅 및 유지보수도 용이합니다.
- 객체의 상태가 고정되어 있어 여러 스레드에서 동시에 접근해도 동기화가 필요 없으므로 thread-safe 하며, 병렬 프로그래밍에 매우 유리합니다.
(MSA와 같은 분산 환경에서 DTO나 VO(Value Object)를 불변 객체로 설계하는 대표적인 이유입니다.)
- 상태가 변하지 않기 때문에 연산 결과를 캐싱하여 재사용하기 쉽고, 동일한 입력에 대해 동일한 객체를 공유할 수 있어 메모리 사용과 성능 측면에서 유리합니다.
- 불변 객체는 상태 변화가 없어 장기적으로 참조가 유지되기 쉬우며, 이로 인해 GC(Garbage Collection)에서 재생성이나 스캔 대상이 되는 빈도가 낮아지는 경향이 있어 미세한 성능 향상에 기여할 수 있습니다.
결론
자바에서 커스텀 클래스의 멤버 변수로 배열이나 리스트를 사용할 때 불변성을 보장하려면, 배열은 Arrays.copyOf()를 이용한 방어적 복사(defensive copy) 방식으로 생성자와 getter에서 모두 복사본을 사용하여 외부 변경을 차단해야 합니다. 리스트의 경우, 가장 간단한 방법은 Java 9 이상에서 제공되는 List.of()를 사용하는 것이며, Java 8 이하에서는 원본 리스트를 new ArrayList<>(...)로 복제한 후 Collections.unmodifiableList()로 감싸는 방식이 안전한 불변 리스트를 구성하는 방법입니다. 이와 같은 불변화 처리는 캡슐화 보호, 부수 효과 제거, thread-safety 확보, 캐싱 최적화, GC 효율성 등 다양한 측면에서 객체 설계의 안정성과 성능을 크게 향상시킵니다.
'CS' 카테고리의 다른 글
CS 스터디 (Java) 6 - 싱글톤 패턴은 어떨 때 사용할까요? (0) | 2025.05.15 |
---|---|
CS 스터디 (Java) 5 - Volatile 키워드에 대해 설명해주세요. (0) | 2025.05.15 |
CS 스터디 (Java) 4 - Autoboxing/Unboxing이 성능에 미치는 영향을 설명하고 이를 최적화할 수 있는 방법을 말하시오. (0) | 2025.05.14 |
CS 스터디 (Java) 3 - JVM 의 메모리 영역과 할당 시점은? (0) | 2025.05.13 |
CS 스터디 (Java) 1 - try with resources를 설명해주세요. (0) | 2025.05.12 |