본문으로 건너뛰기
개발 머꼬
개발 노트Java
hohyeon.dev25

제네릭 varargs 메서드에서 힙 오염 경고가 뜬 이유

  • #Engineering Note
  • #Generics
  • #Java

문제 발생

제네릭 타입을 받는 varargs 메서드를 정의했더니, 컴파일러가 "unchecked generic array creation" 경고를 냈습니다.

@SafeVarargs
static <T> List<T> listOf(T... items) {
    return Arrays.asList(items);
}

원인 분석

두 가지 Java의 설계가 겹치면서 생기는 문제입니다.

  1. **배열은 공변(covariant)**입니다 — String[]Object[]의 서브타입으로 취급되어, Object[]가 필요한 곳에 String[]을 넣을 수 있습니다. 이건 런타임에 타입 검사를 하기 때문에 안전합니다(잘못된 타입을 넣으려 하면 ArrayStoreException이 발생합니다).
  2. 제네릭은 타입 소거됩니다 — List<String>List<Integer>는 런타임에 둘 다 그냥 List입니다.

T... items(varargs)는 내부적으로 T[] 배열로 컴파일됩니다. 그런데 T는 타입 소거로 인해 런타임에 Object로 취급되므로, 실제로는 Object[] 배열이 만들어집니다. 문제는 이 배열이 제네릭 타입 정보를 전혀 갖고 있지 않으면서도 T[]인 척한다는 점 — 공변 배열의 런타임 타입 체크가 제네릭 앞에서는 무력화된다는 뜻입니다. 이 상태를 **힙 오염(heap pollution)**이라 부릅니다 — 실제로는 다른 타입의 원소가 섞여 들어가도 컴파일러가 이를 완전히 막아주지 못합니다.

해결 방안

  1. 메서드가 varargs 배열에 값을 저장하지 않고 읽기만 한다면 안전합니다 — 이 경우 @SafeVarargs 애너테이션을 붙여 이 메서드가 안전함을 명시적으로 보증하고, 호출부에서 뜨는 경고를 억제합니다. @SafeVarargsstatic, final, private, 또는 생성자 메서드에만 붙일 수 있습니다 — 오버라이드 가능한 메서드에는 이 보증을 할 수 없기 때문입니다.
  2. varargs 배열에 값을 쓰는 메서드라면 힙 오염이 실제로 발생할 수 있으므로 @SafeVarargs를 붙이면 안 됩니다 — 애초에 설계를 바꿔서 배열 대신 List<T>를 매개변수로 받는 것을 고려합니다.
  3. 라이브러리 공개 API를 설계할 때, 제네릭 varargs 메서드는 호출부에서도 비슷한 경고가 뜰 수 있다는 점을 감안해 문서화합니다.
  4. 이 문제는 Java 언어 자체의 배열/제네릭 설계가 만나는 지점의 근본적인 한계입니다 — "경고를 없애는 방법"을 찾기보다 그 메서드가 정말 타입 안전한지 먼저 판단하는 것이 순서입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.