# 가변인수는 신중히 사용하라

OpenJdk azul-11을 기반으로 작성하였습니다. 오개념이나 잘못된 부분이 있으면 dev.hyeonic@gmail.com로 많은 피드백 부탁드립니다!

# 팩토리 메서드

Java 9에서는 작은 컬렉션 객체를 쉽게 만들 수 있는 팩토리 메서드를 제공한다. 그 중 List 인터페이스를 살펴보면 아래와 같은 메서드들이 오버로딩 되어 제공되는 것을 확인할 수 있다.

public interface List<E> extends Collection<E> {
    ...
    static <E> List<E> of() {...}
    static <E> List<E> of(E e1) {...}
    static <E> List<E> of(E e1, E e2) {...}
    static <E> List<E> of(E e1, E e2, E e3) {...}
    static <E> List<E> of(E e1, E e2, E e3, E e4) {...}
    static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {...}
    ...
    static <E> List<E> of(E... elements) {...}
    ...
}

Java에는 다중 요소를 받을 수 있는 가변인수 메서드를 가지고 있다. static <E> List<E> of(E... elements)가 바로 가변 인수를 활용한 팩토리 메서드이다. 하지만 요소의 개수마다 메서드가 작성되어 있다. 추가적인 조사를 통해 이유에 대해 알아보려 한다.

# 가변인수 메서드

Java 1.5부터 매개변수의 개수를 동적으로 지정할 수 있는 가변인수(varargs) 메서드가 등장하였다. 가변인수 메서드는 명시한 타입의 인수를 0개 이상 받을 수 있다. 이러한 메서드를 호출하면 인수의 개수와 길이가 같은 배열을 만들고 이 배열에 저장하여 가변인수 메서드에게 건네준다.

아래는 가변인수를 사용한 sum 메서드의 간단한 예시이다. 매개 변수 타입앞에 ... 키워드를 추가하여 구현할 수 있다.

static int sum(int... args) {
    int sum = 0;
    for (int arg : args) {
        sum += arg;
    }
    return sum;
}
System.out.println(sum(1, 2, 3)); // 6
System.out.println(sum(1, 2, 3, 4, 5)); // 15
System.out.println(sum(1, 2, 3, 4, 5, 6, 7)); // 28

위 처럼 가변인수 메서드는 인수 개수가 정해지지 않았을 때 아주 유용하다.

# List의 다양한 of 메서드

이제 가변인수 메서드의 이점을 확인하였다. 그렇다면 List 인터페이스는 왜 다양한 of 메서드를 사용하는 것인가?

정답은 바로 성능에 대한 문제 때문이다. 가변인수 메서드는 호출될 때 마다 내부적으로 배열을 새로 하나 할당하고 초기화를 진행한다. 이것을 고려하기 위해 List에서는 인수가 0개부터 10인 것 까지 11개의 추가적인 메서드오버로딩하여 해결하였다. 이제 고정된 숫자의 요소를 API로 정의하므로 추가적인 비용을 해결할 수 있다.

이제 대부분의 상황에서 가변인수 메서드를 사용하지 않고 특정 상황에서만 가변인수 메서드를 사용할 것이다.

# (번외) 인수가 1개 이상이어야 할 때

만약 1개 이상의 수 중에 최소값을 구하기 위한 요구사항이 있다고 가정한다. 최소값을 구하기 위해서는 적어도 1개의 인수가 필수적으로 필요하다. 하지만 가변인수 메서드를 사용하게 되면 인수 0개를 허용하게 된다. 또한 런타임 시점에 검증을 진행하기 때문에 답을 구할 수 없게 된다.

static int min(int... args) {
    if (args.length == 0) {
        throw new IllegalArgumentException("인수가 1개 이상 필요합니다.");
    }
    ...
}

이러한 상황에서는 첫 번째로 평범한 매개변수를 받고, 가변인수는 두 번째로 받으면 앞서 언급한 문제를 해결할 수 있다.

static int min(int firstArg, int... args) {
    int min = firstArg;
    for (int arg : args) {
        min = Math.min(min, arg);
    }
    return min;
}

# References

라울-게이브리얼 우르마, 마리오 푸스코, 앨런 마이크로프트, 『Modern Java in Action』, 한빛미디어(2019), p278.
조슈아 블로크, 『Effective Java』, 인사이트(2018), p320. 아이템 53 가변인수는 신중히 사용하라

#Java #varargs #아이템 53
last updated: 12/28/2021, 5:55:45 PM