가변인수와 제네릭의 조합
가변인수(... 문법)와 제네릭은 조합해서 사용하지 않는 편이 좋다. 가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 하나 자동으로 만들어지기 때문에, 가변인수 매개변수에 제네릭이나 매개변수화 타입이 포함되면 알기 어려운 컴파일 경고가 발생한다.
Possible heap pollution from parameterized vararg tpye List
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
}
다음 코드는 마지막 줄에서 보이지 않는 형변환이 숨어 있기 때문에 예외가 던져진다. 그럼에도 불구하고 유용성의 이점 때문에 제네릭 가변인수를 허용하고 있고 이러한 에러를 감추기 위해 @SafeVargs 어노테이션을 지원한다.
@SafeVarargs
작성자가 이 메서드가 타입 안전함을 보장할 때 사용하는 어노테이션으로 타입 안전함이 보장될 때만 사용해야 하며 재정의할 수 없는 메서드에만 달아야 한다.
타입 안전이 보장되는 경우는 다음과 같다.
- 가변인수를 리턴하지 않는 경우로 다음과 같은 상황에서는 사용하면 안된다.
static <T> T[] toArray(T... args) {
return args;
}
- 가변인수 매개변수 배열에 새로운 것을 저장하지 않는다.
@SafeVarargs
static <T> List<T> flatten(List<? extends T>... lists) {
List<T> result = new ArrayList<>();
for(List<? extends T> list : lists) {
results.add(list);
}
return result;
}