본문 바로가기

Reading/Effective Java

[Effective-Java] Item 53. 가변인수는 신중히 사용하라

가변인수


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

가변인수 메소드를 호출하면 인수의 개수와 길이가 같은 배열을 만들고 인수들을 이 배열에 저장해 가변인수 메소드에 건넨다.

 

static int sum(int... args) {
    int sum = 0;
    for (int arg : args)
        sum += arg;
    return sum;
}
public static void main(String[] args) {
    System.out.println(sum()); // 0
    System.out.println(sum(1)); // 1
    System.out.println(sum(1,2)); // 3
    System.out.println(sum(1,2,3)); // 6
    System.out.println(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // 55
}

 

인수 개수는 런타임에 자동 생성된 배열의 길이를 통해 알 수 있다.

하지만 인수가 1개 이상이여야 할 때도 있는데, 예를들어 최소값을 찾는 메소드의 경우에는 매개변수를 보내지않으면 런타임에러다.

코드도 지저분하고 arg 유효성 검사는 명시적으로 유지, min의 초기값을 Integer.MAX_VALUE로 설정 않고는 더 명료한 for-Each문을 사용할 수도 없다. 그래서 코드를 아래와 같이 변경해서 사용하면 된다.

 

// 가변인수만 쓴다면 유효성 검사 명시성을 이용해 코드가 지저분해지고 인수 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;
}

// 매개변수 2개로, 무조건 1개는 입력하도록 설정해 유효성 검사가 없고 코드도 깔끔해지며 인수 1개 런타임에러가 없다.
static int min(int firstArg, int... remainingArgs) {
    int min = firstArg;
    for (int arg : remainingArgs)
        if (arg < min)
            min = arg;
    return min;
}

 

위와같이, 가변인수는 인수 개수가 정해지지 않았을 때 아주 유용하다.

 

PrintStream의 printf는 가변인수를 사용한다.

 

&nbsp;Class 클래스는 여러 메소드에서 가변인수를 사용한다.

 

대표적으로 가변인수 덕을 보고있는 자바 라이브러리의 함수를 택해보자면  printf와 핵심 리플렉션 기능이다.

가변인수가 자바 1.5부터 들어왔기때문에, 자바 1.4에서 Class와 PrintStream의 메소드를 확인하려했으나,

자바독스에서 자바 7부터 확인할 수 있어, 예측컨대 기존에는 점진적 패턴을 사용했거나 배열을 사용했을 것으로 보인다.

 

 

가변인수의 성능


성능에 민감한 상황이라면 가변인수가 걸림돌이 될 수 있다.가변 인수 메소드는 호출될 때마다 배열을 새로 할당하고 초기화하고, 이 비용을 감당할 순 없지만 유연성이 필요할 때 점진적 패턴을 사용하면 된다.

 

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){}

 

메소드 호출의 95%가 인수 3개 이하를 사용한다면 위 코드와 같이 정해두면 된다.

그러면 메소드 호출중 5%만이 배열을 생성할 것이고, 큰 이득은 없지만 성능에 아주 민감한 특수한 상황에서는 좋은 방법이다.

 

EnumSet은 정적 팩토리도 이 기법을 사용해 열거 타입 집합 생성 비용을 최소화한다. EnumSet은 of 메소드에 이 기법을 사용한다.

 

 

EnumSet은 비트 필드를 대체하면서 성능까지 유지해야 해서 적절하게 사용한 예다.

 

인수 개수가 일정하지 않은 메소드를 정의해야 한다면 가변인수가 반드시 필요하고, 메소드를 정의할 때 필수 매개변수는 가변인수 앞에 두고, 가변인수를 사용할 땐 성능 문제까지 고려하자.

 

 

 

* 위 글은 EffectiveJava 3/E 책 내용을 정리한 글로, 저작권 관련 문제가 있다면 댓글로 남겨주시면 즉각 삭제조치 하겠습니다.