kingsubin

아이템 53. 가변인수는 신중히 사용하라 본문

책/이펙티브자바

아이템 53. 가변인수는 신중히 사용하라

kingsubin 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이 이 기법의 좋은 예시이다.

결론

  • 인수 개수가 일정하지 않은 메서드를 정의한다면 가변인수가 반드시 필요하다.
  • 메서드 정의시 필수 매개변수는 가변인수 앞에 두자.
  • 가변인수 사용시 성능문제도 고려해보자.
0 Comments
댓글쓰기 폼