제네릭 varargs 메서드에서 힙 오염 경고가 뜬 이유
문제 발생
제네릭 타입을 받는 varargs 메서드를 정의했더니, 컴파일러가 "unchecked generic array creation" 경고를 냈습니다.
@SafeVarargs
static <T> List<T> listOf(T... items) {
return Arrays.asList(items);
}원인 분석
두 가지 Java의 설계가 겹치면서 생기는 문제입니다.
- **배열은 공변(covariant)**입니다 —
String[]은Object[]의 서브타입으로 취급되어,Object[]가 필요한 곳에String[]을 넣을 수 있습니다. 이건 런타임에 타입 검사를 하기 때문에 안전합니다(잘못된 타입을 넣으려 하면ArrayStoreException이 발생합니다). - 제네릭은 타입 소거됩니다 —
List<String>과List<Integer>는 런타임에 둘 다 그냥List입니다.
T... items(varargs)는 내부적으로 T[] 배열로 컴파일됩니다. 그런데 T는 타입 소거로 인해 런타임에 Object로 취급되므로, 실제로는 Object[] 배열이 만들어집니다. 문제는 이 배열이 제네릭 타입 정보를 전혀 갖고 있지 않으면서도 T[]인 척한다는 점 — 공변 배열의 런타임 타입 체크가 제네릭 앞에서는 무력화된다는 뜻입니다. 이 상태를 **힙 오염(heap pollution)**이라 부릅니다 — 실제로는 다른 타입의 원소가 섞여 들어가도 컴파일러가 이를 완전히 막아주지 못합니다.
해결 방안
- 메서드가 varargs 배열에 값을 저장하지 않고 읽기만 한다면 안전합니다 — 이 경우
@SafeVarargs애너테이션을 붙여 이 메서드가 안전함을 명시적으로 보증하고, 호출부에서 뜨는 경고를 억제합니다.@SafeVarargs는static,final,private, 또는 생성자 메서드에만 붙일 수 있습니다 — 오버라이드 가능한 메서드에는 이 보증을 할 수 없기 때문입니다. - varargs 배열에 값을 쓰는 메서드라면 힙 오염이 실제로 발생할 수 있으므로
@SafeVarargs를 붙이면 안 됩니다 — 애초에 설계를 바꿔서 배열 대신List<T>를 매개변수로 받는 것을 고려합니다. - 라이브러리 공개 API를 설계할 때, 제네릭 varargs 메서드는 호출부에서도 비슷한 경고가 뜰 수 있다는 점을 감안해 문서화합니다.
- 이 문제는 Java 언어 자체의 배열/제네릭 설계가 만나는 지점의 근본적인 한계입니다 — "경고를 없애는 방법"을 찾기보다 그 메서드가 정말 타입 안전한지 먼저 판단하는 것이 순서입니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.