가변인수 ...
는 매서드를 사용하는 클라이언트에서 파라미터를 몇개 보낼지 선택하는 것
static void dangerous(List<String>... stringLists) {}
가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어짐
이 배열은 내부로 감춰져야 하는데, 클라이언트에 공개되면서 문제가 발생할 수 있음
특히 varargs
변수에 제네릭이나 매개변수화 타입이 포함되면 알기 어려운 컴파일 경고가 발생
static void dangerous(List<String>... stringLists) {
List<Integer> intList = List.of(42);
Object[] objects = stringLists;
objects[0] = intList; // 힙 오염 발생
String s = stringLists[0].get(0); // ClassCastException
}
위와같이 (List<String>... stringLists)
로 선언 시 경고가 발생함.
→ 제네릭을 사용한 가변인자 떄문에 힙 메모리 가 오염될 수 있다는 메세지
문제가 발생하는 과정
List
의 배열을 Object
에 할당한다.
배열은 공변이기 때문에 List
의 배열을 Object
배열에 할당할 수 있다.
objects[0] = intList
처럼 Object
배열이기 때문에 List<Integer>
를 할당할 수 있다.
stringLists[0].get(0)
코드를 실행할 때 에러가 발생한다.
stringLists[0].get(0)
코드 실행시 컴파일 시 String
으로 캐스팅하는 코드를 넣어준다.
stringLists
는 List<String>...
타입이기 떄문이다.
→ 제네릭을 사용하는 이유는 컴파일 타입부터 런타임까지 타입 안정성 을 확보하는 것인데 런타임에 타입 안정성 이 깨지게 되
작성자가 그 메서드가 타입 안전함을 보장한다고 표시해줄 수 있는 장치
컴파일러는 이 애너테이션을 확인하여 경고를 표시하지 않음
메서드가 안전한지 확신하는 방법
제네릭이나 매개변수화 타입의 varagrs 매개변수를 받는 모든 메서드에 @SafeVarargs를 달자
→ 안전하지 않은 varargs메서드는 작성하지 말자!!
static <T> List<T> flatten(List<List<? extends T>> lists) {
List<T> result = new ArrayList<>();
for (List<? extends T> list : lists)
result.addAll(list);
return result;
}