[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 애너테이션을 달 것