item 53 : 가변인수는 신중히 사용하라.
1. 잘못 구현된 가변 인수 예시
문제 코드:
이 메서드는 가변 인수로 0개 이상의 인수를 받을 수 있다. 만약 아무 인수도 전달되지 않는 경우, 런타임에 예외(
IllegalArgumentException
)가 발생한다.런타임 예외는 코드를 실행하기 전까지 문제를 발견할 수 없으므로, 컴파일 타임에 이 문제가 감지되지 않는다.
또한, for-each문 대신 일반 for문이 사용되어 코드가 불필요하게 복잡하고 덜 깔끔하다.
2. 올바른 가변 인수 구현
올바른 구현 예:
이 방법에서는 첫 번째 매개변수를 일반 매개변수로 두고, 나머지는 가변 인수로 받는다. 이로 인해 최소 하나 이상의 인수가 필요함을 컴파일 타임에 보장할 수 있다.
즉, 아무런 인수를 주지 않으면 컴파일러가 오류를 표시하므로 런타임에 실패할 가능성을 줄여든다.
또한, for-each문을 사용하여 코드가 더 간결하고 가독성이 높다.
3. 가변 인수와 성능 최적화
가변인수란?
가변 인수(Varargs):
가변 인수는 매개변수의 개수가 유동적인 경우에 유용하며, 자바에서는
int... args
와 같이 선언하여 사용한다.가변 인수를 사용하는 메서드는 호출 시 배열을 생성하고 그 배열에 인수를 저장하기 때문에, 반복적인 호출 시 성능 저하가 발생할 수 있다.
성능 최적화와 코드 안정성:
올바르게 가변 인수를 사용하는 것은 코드의 안정성을 높이는 데 기여한다. 필수 매개변수를 일반 매개변수로 정의하면 컴파일 타임에 필수 조건이 보장된다.
성능 최적화도 중요하다. 특히, 자주 호출되는 메서드에서는 불필요한 배열 할당을 최소화함으로써 성능을 개선할 수 있다.
가변 인수의 성능 문제:
가변 인수는 메서드가 호출될 때마다 배열을 할당하고 초기화한다. 이 과정은 큰 비용이 아닐 수도 있지만, 성능 최적화가 필요한 경우에는 주의가 필요하다.
성능 최적화 방법:
위와 같은 방식으로 다양한 매개변수 개수에 맞는 메서드를 오버로딩한다.
대부분의 호출이 3개 이하의 인수를 사용하는 경우라면, 인수 3개까지는 배열을 생성하지 않도록 함으로써 성능을 최적화할 수 있다.
가변 인수는 4개 이상의 인수를 받을 때만 배열을 생성하도록 하여, 배열 생성의 오버헤드를 줄인다.
실제 사용 예시 -
EnumSet
:EnumSet
의 정적 팩터리 메서드도 이와 같은 방식으로 최적화되어 있다. 예를 들어, 4개 이상의 인수를 받는 경우에만 추가적인 처리를 하여 불필요한 리소스 사용을 줄인다.
근데.. .앞장에서 다중 정의는 신중히 쓰라고 하지 않았나? 성능 최적화를 위해 메서드 오버로딩?
맞습니다, 이 부분은 다소 모순적일 수 있습니다. 앞에서 다중 정의(오버로딩, Overloading)는 혼란을 초래할 수 있으므로 사용을 신중히 해야 한다고 했지만, 여기에서는 성능 최적화를 위해 매개변수 개수에 따른 메서드 오버로딩을 제안하고 있습니다.
이 모순은 특정 상황에서의 트레이드오프(trade-off)로 이해할 수 있습니다. 각각의 상황에서 무엇이 더 중요한지에 따라 접근 방식이 달라지기 때문입니다.
오버로딩을 피해야 하는 이유
혼란 가능성: 오버로딩은 메서드가 컴파일타임에 선택되기 때문에, 어떤 메서드가 호출될지 명확하지 않은 경우 혼란을 초래할 수 있습니다. 특히, 매개변수의 타입이 근본적으로 다르지 않거나 오토박싱, 제네릭 등의 요소가 개입되면 개발자가 의도한 대로 동작하지 않을 수 있습니다.
사용자 혼란: API 사용자가 어떤 메서드가 호출될지 예상하기 어려운 경우, 개발자가 그 동작을 잘못 이해하고 사용하는 경우가 생길 수 있습니다.
오버로딩을 사용하는 경우 - 성능 최적화
성능 최적화의 필요성: 가변 인수는 메서드가 호출될 때마다 배열을 생성하고 초기화하기 때문에, 성능이 중요한 경우 이 비용을 줄이는 것이 필요합니다.
이때, 자주 호출되는 메서드의 경우 매개변수의 개수별로 오버로딩된 메서드를 제공함으로써 불필요한 배열 생성을 피할 수 있습니다. 대부분의 호출이 인수가 적은 경우라면 이러한 최적화는 성능 개선에 크게 기여할 수 있습니다.
EnumSet의 최적화 예시
EnumSet
의 정적 팩터리 메서드도 이러한 방식으로 최적화되어 있습니다. 인수가 적은 경우에는 별도의 처리를 통해 최적화하고, 4개 이상의 인수에 대해서만 배열을 생성하거나 추가적인 처리를 합니다.이는 성능 최적화와 코드의 효율성 때문에 도입된 예시로, 오버로딩을 사용하여 특정 성능 이슈를 해결하려는 것입니다.
요약 및 트레이드오프 설명
오버로딩을 피해야 할 때:
메서드 간의 선택이 명확하지 않은 경우: 예를 들어, 서로 다른 매개변수 타입이 있지만 이들 사이에 오토박싱이나 제네릭과 같은 요소가 개입되면 혼란을 일으킬 수 있으므로, 오버로딩을 피하는 것이 좋습니다.
API의 가독성과 직관성이 중요한 경우: API 사용자에게 명확하게 어떤 메서드가 호출될지 보장할 수 없는 경우 오버로딩은 지양해야 합니다.
오버로딩을 사용해도 좋은 경우:
성능 최적화가 중요한 경우: 예를 들어, 가변 인수의 배열 생성이 반복될 때의 성능 오버헤드를 줄이기 위해서는 매개변수 개수에 따른 오버로딩이 도움이 될 수 있습니다.
이런 경우, 오버로딩을 통해 자주 호출되는 메서드에서 최적화된 코드 경로를 제공하여 성능을 개선할 수 있습니다.
결론적으로, 오버로딩은 상황에 따라 장단점이 있으며, 어떤 상황에서는 피해야 하지만 성능 최적화와 같이 특정한 이유가 있다면 신중하게 사용할 수 있습니다. 중요한 것은 오버로딩으로 인해 발생할 수 있는 혼란과 장점을 잘 저울질하여 사용 여부를 결정하는 것입니다.
📚 핵심 정리
가변 인수를 사용하자:
인수 개수가 일정하지 않은 메서드를 정의할 때 가변 인수를 사용하는 것이 좋다.
필수 매개변수가 있다면:
필수 매개변수가 있다면, 가변 인수 앞에 일반 매개변수로 두어야 한다. 이를 통해 컴파일 타임에 오류를 감지할 수 있고, 잘못된 호출을 방지할 수 있다.
성능 고려:
가변 인수를 사용할 때는 성능 문제도 고려해야 한다. 필요한 경우, 매개변수의 개수에 따라 다른 메서드를 오버로딩하여 배열 할당 비용을 줄일 수 있다.
결론적으로, 가변 인수를 사용할 때는 필수 매개변수를 앞에 두어 컴파일 타임에 오류를 잡도록 하고, 성능 최적화가 필요하다면 오버로딩을 통해 배열 생성의 오버헤드를 줄이는 방식을 사용하는 것이 좋다. 이렇게 하면 코드의 가독성과 안정성, 그리고 성능을 모두 개선할 수 있다.
참고 및 출처
Last updated