[Effective Java 3/e] 아이템 32 - 제네릭과 가변인수를 함께 쓸 때는 신중하라
아이템 32 - 제네릭과 가변인수를 함께 쓸 때는 신중하라
제네릭 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("좋은", "빠른", "저렴한");
}
- ClassCastException 발생
- 배열의 타입은 Object 인데 반환 시 String[]로 형변환하는 코드가 자동 생성된다.
안전한 제네릭 매개변수 사용
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("좋은", "빠른", "저렴한");
}
- 정적 팩터리 메서드인 List.of를 사용하면 임의개수의 인수를 넘길 수 있다.
- 가능한 이유 - List.of에 @SafeVarargs 애너테이션이 달려 있기 때문
- @SafeVarargs - 메서드 작성자가 그 메서드가 타입 안전함을 보장하는 애너테이션
정리
- 가변인수와 제네릭은 궁합이 좋지 않다.
- 가변인수 기능은 배열을 노출하여 추상화가 완벽하지 못하고, 배열과 제네릭의 타입 규칙이 서로 다르기 때문
- 메서드가 타입 안전하면 @SafeVarargs 애너테이션을 달 것