본문 바로가기
공부/이펙티브자바

[이펙티브자바]item 32.제네릭과 가변인수를 함께 쓸 때는 신중하라.

by 띵커베르 2023. 2. 16.
728x90
  • varargs 매개변수에 제네릭이나 매개변수화 타입이 포함되면 알기 어려운 컴파일 경고가 발생한다.
public class Dangerous {
    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
    }

    public static void main(String[] args) {
        dangerous(List.of("There be dragons!"));
    }
}
  • 매개변수화 타입의 변수가 다른 객체를 참조하면 힙 오염이 발생한다.
  • 위 코드는 한줄한줄만 본다면 이상이 없지만 값을 꺼낼때 문제가 발생한다.
  • 즉 제네릭 varargs 배열 매개변수에 값을 저장하는것은 안전하지 않다.
  • @SafeVarargs 애너테이션으로 메서드 타입이 안전함을 보장하는 방법도 있다.
    • 메서드가 이 배열에 아무것도 저장하지 않는다.
    • 그 배열(혹은 복제본)을 신뢰할 수 없는 코드에 노출하지 않는다.
  • 임의 개수의 리스트를 인수로 받아, 받은 순서대로 그 안의 모든 원소를 하나의 리스트로 옮겨 담아 반환하는 방법도 있다.
public class FlattenWithVarargs {
    @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;
    }

    public static void main(String[] args) {
        List<Integer> flatList = flatten(
                List.of(1, 2), List.of(3, 4, 5), List.of(6, 7));
        System.out.println(flatList);
    }
}
  • arargs 매개변수를 List 매개변수로 바꿀 수도 있다.
public class FlattenWithList {
    static <T> List<T> flatten(List<List<? extends T>> lists) {
        List<T> result = new ArrayList<>();
        for (List<? extends T> list : lists)
            result.addAll(list);
        return result;
    }

    public static void main(String[] args) {
        List<Integer> flatList = flatten(List.of(
                List.of(1, 2), List.of(3, 4, 5), List.of(6, 7)));
        System.out.println(flatList);
    }
}
  • 기타
    • 예제에 ThreadLocalRandom 클래스 사용이 보이는데 ThreadLocal에 대해서도 알아보면 좋을 듯 하다.
728x90

댓글