ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템 53. 가변인수는 신중히 사용하라
    책/이펙티브자바 2021. 9. 17. 17:33

    가변인수 (varargs) 메서드는 명시한 타입의 인수를 0개 이상 받을 수 있다.

    가변인수 메서드 호출 시 인수의 개수와 길이가 같은 배열을 만들고 인수들을 이 배열에 저장하여 가변인수 메서드에 건네준다.

    // 가변인수 사용법
    static int sum(int... args) {
        int sum = 0;
        for (int arg : args) {
            sum += args;
        }
        return sum;
    }

    예를 들어 최솟값을 구하는 메서드를 작성한다면 인수가 1개 이상이어야 하는데 0개도 받을 수 있게 설계하면 에러가 날 수 있다.

    // BAD - 인수가 1개 이상이어야 할 때 가변인수 사용법
    static int min(int... args) {
        if (args.length == 0) throw new IllegalArgumentException("인수 1개 이상 필요");
        int min = args[0];
        for (int i = 1; i < args.length; i++) {
            if (args[i] < min) min = args[i];
        }
        return min;
    }

    그래서 이런식으로 0개를 받으면 IllegalArgumentException 를 던지게 했다.

    근데 이 책에서 계속 말하듯 오류는 빨리 잡아야하는데 이런식으로 하면 런타임에 실패한다.

    거기다 코드도 지저분하다. 다른 방법을 사용하자.

    // GOOD - 인수가 1개 이상이어야 할 때 가변인수 사용법
    static int min(int firstArg, int... remainingArgs) {
        int min = firstArg;
        for (int arg : remainingArgs) {
            if (arg < min) min = arg;
        }
        return min;
    }

    이렇게 첫 번째로는 평번한 매개변수를 받고, 가변인수를 두 번째로 받으면 문제가 깔끔히 사라진다.

    이렇듯 가변인수는 인수 개수가 정해지지 않았을때 아주 유용하게 사용할 수 있다.

     

    가변인수는 앞에서 말했듯 호출될 때마다 배열을 새로 하나 할당하고 초기화한다. 그래서 성능에 민감할때 문제가 될 수 있다. 다행히 유연성이 필요할 때 선택할 수 있는 패턴이 있다.

    public void method1() {}
    public void method1(int a1) {}
    public void method1(int a1, int a2) {}
    public void method1(int a1, int a2, int a3) {}
    public void method1(int a1, int a2, int a3, int... rest) {}

    예를 들어 메소드 호출의 95% 정도가 인수를 3개 이하로 쓴다면 이런식으로 다중정의하는 것이다.

    95% 호출은 가변인수를 사용하지 않아 배열을 생성하지 않고 5% 호출만 배열을 생성하는 것이다.

    // EnumSet
    public static <E extends Enum<E>> EnumSet<E> of(E e) {
      EnumSet<E> result = noneOf(e.getDeclaringClass());
      result.add(e);
      return result;
    }
    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
      EnumSet<E> result = noneOf(e1.getDeclaringClass());
      result.add(e1);
      result.add(e2);
      return result;
    }
    
    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3) {
      EnumSet<E> result = noneOf(e1.getDeclaringClass());
      result.add(e1);
      result.add(e2);
      result.add(e3);
      return result;
    }
    
    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4) {
      EnumSet<E> result = noneOf(e1.getDeclaringClass());
      result.add(e1);
      result.add(e2);
      result.add(e3);
      result.add(e4);
      return result;
    }
    
    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4,
                                                      E e5) {
      EnumSet<E> result = noneOf(e1.getDeclaringClass());
      result.add(e1);
      result.add(e2);
      result.add(e3);
      result.add(e4);
      result.add(e5);
      return result;
    }
    
    @SafeVarargs
    public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
      EnumSet<E> result = noneOf(first.getDeclaringClass());
      result.add(first);
      for (E e : rest)
          result.add(e);
      return result;
    }

    Java API 중 EnumSet이 이 기법의 좋은 예시이다.

    결론

    • 인수 개수가 일정하지 않은 메서드를 정의한다면 가변인수가 반드시 필요하다.
    • 메서드 정의시 필수 매개변수는 가변인수 앞에 두자.
    • 가변인수 사용시 성능문제도 고려해보자.
킹수빈닷컴