가변인수는 신중히 사용하라
가변인수 유의점
public class VarArgsExample {
public static void printNumbers(String message, int... numbers) {
System.out.println(message);
for (int num : numbers) {
System.out.print(num + " ");
}
System.out.println();
}
}
가변인수(varargs) 메서드는 명시한 타입의 타입의 인수를 0개 이상 받을 수 있다.
위 코드에서 가변인수는 'int…'로 선언되어 있다. 이 가변인수를 포함한 메서드를 호출하면 가장 먼저 인수의 개수와 길이가 같은 배열을 만들고 인수들을 이 배열에 저장하여 가변인수 메서드에 건넨다.
좀 더 예시를 살펴보자.
static int sum(int... args){
int sum = 0;
for (int arg : args){
sum += arg;
}
return sum;
}
위 코드는 입력받은 int 인수들의 합을 계산하는 가변인수 메서드다.
sum(1, 2, 3)은 6을 반환하고, sum()은 0을 반환하게 된다.
이 때 인수가 1개 이상이어야 할 때도 있는데, 예컨대 최솟값을 찾는 메서드에서 인수를 0개만 받을 수 있도록 설계하는 것은 좋지 않다.
인수의 갯수는 런타임에 자동으로 생성된 배열의 길이로 알 수 있다.
인수가 1개 이상이어야 하도록 0개일 때 예외를 던지는 방법은 어떨까?
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개만 넣어 호출하면 컴파일시점이 아닌 런타임에 실패한다는 점이다.
또한 args의 유효성 검사를 명시적으로 해야하고, min의 초깃값을 Integer.MAX_VALUE로 설정하지 않고는 명료한 for-each 문을 사용할 수 없다.
이를 개선하려면 어떻게 할까?
static int min(int firstArg, int... remainArgs) {
int min = firstArg;
for(int arg : remainArgs) {
if (arg < min){
min = arg;
}
return min;
}
}
첫 번째로 매개변수로 평범한 매개변수를, 두 번째로 가변인수를 받는다면 가변인수만을 매개변수로 받을 때의 문제점을 개선할 수 있다.
이처럼 가변인수는 인수의 갯수가 정해지지 않았을 때 유용하다.
가변인수의 성능 이슈
하지만 성능에 민감한 상황이라면 가변인수가 걸림돌이 될 수 있다.
이유는 호출될 때마다 배열을 새로 하나 할당하고 초기화하기 때문이다.
이 작업의 비용을 감당할 수는 없지만, 가변인수의 유연성이 필요할 때 선택할 수 있는 좋은 패턴이 있다.
- 메서드 다중정의를 활용한 경우
예를 들어 해당 메서드 호출의 95%가 인수를 3개 이하로 사용한다고 가정해보자.
public void foo() { }
public void foo(int a1) { }
public void foo(int a1, int a2) { }
public void foo(int a1, int a2, int a3) { }
public void foo(int a1, int a2, int a3, int... rest) { }
그렇다면 위와 같이 인수가 0개인 것부터 4개인 것까지, 총 5개의 메서드를 다중정의 하자.
이는 마지막으로 정으된 다중정의 메서드가 인수 4개 이상인 5%의 호출을 담당하는 것이다.
item52의 다중정의 시에 주의할 점을 염두에 둔다면 꼭 필요한 특수 상황에서 도움이 될 것이다.
참고자료
- 이펙티브자바 3판