- 가변인수는 메서드에 넘기는 인수의 개수를 클라이언트가 조절할 수 있게 해준다.
- 가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어진다.
- 내부로 감춰야 했을 해당 배열을 클라이언트에 노출되는 문제가 생긴다.
- 실체화되지 않는 제네릭과 매개변수화 타입을 메소드의 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
}
Arrays.asList(T...a)
, Collections.addAll(Collection<? super T> c, T... elements)
, EnumSet.of(E first, E... rest)
- 위 예시는 모두 타입 안전하다.
@SuppressWarnings("unchecked")
을 사용해 경고를 숨겨야 했다.지루한 작업, 가독성을 떨어뜨리고 떄로는 진짜 문제를 알려주는 경고를 숨기는 안좋은 결과로 이어지기까지 했다.
@SafeVarargs
애너테이션은 제네릭 가변인수 메서드 작성자가 클라이언트 측에서 발생하는 경고를 숨길 수 있게 해준다.
- 가변인수 메소드를 호출할 때 varargs 매개변수를 담는 제네릭 배열이 만들어진다.
- 메소드가 varargs 매개변수를 담는 배열에 아무것도 저장하지 않을 때
- varargs 배열의 참조가 밖으로 노출되지 않을 때
- 즉, varargs 매개변수 배열이 호출자로부터 그 메소드로 순수하게 인수들을 전달하는 일만 한다면 안전하다.
static <T> T[] toArray(T... args){
return args;
}
static <T> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // 도달할 수 없다.
}
public static void main(String[] args) {
String[] attributes = pickTwo("좋은", "빠른", "저렴한");
System.out.println(Arrays.toString(attributes));
}
- Object[]는 String의 하위 타입이 아니므로 형변환을 실패한다.
@SafeVarargs
static <T> List<T> flatten(List<? extends T>... lists) {
List<T> result = new ArrayList<>();
for (List<? extends T> list : lists)
result.addAll(list);
return result;
}
- varargs 매개변수 배열에 아무것도 저장하지 않는다.
- 그 배열(혹은 복제본)을 신뢰할 수 없는 코드에 노출하지 않는다.
static <T> List<T> flatten(List<List<? extends T>> lists){
List<T> restul = new ArrayList<>();
for(List<? extends T> list : lists)
result.addAll(list);
return result;
}
static <T> List<T> pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return List.of(a, b);
case 1: return List.of(a, c);
case 2: return List.of(b, c);
}
throw new AssertionError();
}
public static void main(String[] args) {
List<String> attributes = pickTwo("좋은", "빠른", "저렴한");
System.out.println(attributes);
}
Reference: