본문 바로가기
effective java

Item32. 제네릭과 가변인수를 함께 쓸 때는 신중하라

by mjjang 2022. 1. 23.

1. 가변 인수(varargs) 메서드와 제네릭은 잘 어울리지 않는다.

static void dangerous(List<String>... stringLists) {
        List<Integer> integerList = Arrays.asList(42);
        Object[] objects = stringLists;
        objects[0] = integerList;
        String s = stringLists[0].get(0); // ClassCastException
}

컴파일 된 class 파일
static void dangerous(List<String>... stringLists) {
        List<Integer> integerList = Arrays.asList(42);
        stringLists[0] = integerList;
        String s = (String)stringLists[0].get(0); Integer -> String 변환 불가
}

마지막 줄에 컴파일러가 생성한 형변환이 숨어 있어 ClassCastException을 던진다.

그래서 제네릭 varargs 배열 매개변수에 값을 저장하는 것은 안전하지 않다.

2. 메서드가 타입 안전한지 보장하는 방법

  1. varargs 매개변수를 담는 제네릭 배열에 아무것도 저장하지 않는다.
  2. 배열의 참조가 밖으로 노출되지 않는다.

varargs 배열에 아무것도 저장하지 않고 타입 안정성을 깨는 예 (1번은 지켰지만 2번을 지키지 못함)

public static void main(String[] args) {
    String[] strings = pickTwo("a", "b", "c");
}

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();
}

private static <T> T[] toArray(T... args) {
    return args;
}

// 컴파일된 class 파일
public static void main(String[] args) {
    String[] strings = (String[])pickTwo("a", "b", "c"); // ClassCastExceptipon
}

pickTwo 메소드를 본 컴파일러는 toArray에 넘길 T 인스턴스 2개를 담을 varargs 배열을 만드는데 이 배열 타입은 Object[]이다. T에 어떠한 값이 들어와도 다 담을 수 있기 때문이다.

위 코드는 별다른 경고 없이 컴파일이 되지만 실행하면 ClassCastException을 던진다.

pickTwo 메소드가 반환한 배열이 Object[] 타입이 String[] 타입으로 변환할 수 없기 때문이다.

public static void main(String[] args) {
    String[] strings = toArray("a", "b", "c");
}

toArray 메소드를 실행한다면 varargs배열 타입을 String으로 추론 가능해서 런타임에도 코드가 정상적으로 작동한다.


  • 디버깅 모드로 실행 (toArray호출, pickTwo호출 비교)

즉 varargs 배열의 참조를 밖으로 노출(toArray에서 리턴)하였기 때문에 이 참조를 사용하는 메소드(pickTwo)는 타입 안정성을 보장 받을 수 없게 된다. (2번 규칙 위반)

예외 사항

  • @SafeVarargs로 어노테이트된 메서드에 넘기는 것은 안전하다.
  • varargs 배열의 일부만을 사용하는 메소드에 넘기는 것도 안전하다.

댓글