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

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

🔹 핵심 요약

✅ 가변인수(varargs)는 메서드에 가변 개수의 인자를 전달할 수 있는 편리한 기능 ✅ 내부적으로 배열을 생성하므로 성능상 이슈가 발생할 수 있음 ✅ 빈 배열이나 null이 들어올 수 있는 상황을 주의해야 함 ✅ 가변인수와 제네릭을 함께 사용할 때 주의가 필요함


📚 필수 개념 정리

💡 가변인수(Varargs)

  • Java 5부터 도입된 기능으로 메서드에 여러 개의 인자를 전달할 수 있게 함

  • 가변인수는 메서드 선언 시 타입 뒤에 ...을 붙여 선언

  • 내부적으로는 배열로 처리됨

  • 메서드 매개변수 중 가장 마지막에 위치해야 함

public void printNumbers(int... numbers) {
    for (int number : numbers) {
        System.out.print(number + " ");
    }
    System.out.println();
}

// 호출 방법
printNumbers(1, 2, 3);       // 여러 인자 전달
printNumbers();              // 인자 없이도 호출 가능
printNumbers(new int[]{4, 5, 6});  // 배열로도 전달 가능

💡 가변인수 동작 방식

  • 컴파일러는 가변인수를 배열로 변환하여 처리

  • 메서드가 호출될 때마다 새로운 배열 생성

  • 위 예시의 printNumbers(1, 2, 3)는 내부적으로 new int[]{1, 2, 3}으로 변환됨


🚨 가변인수의 문제점

📦 성능 이슈

👉 가변인수 메서드는 호출될 때마다 배열을 새로 할당하고 초기화하므로, 성능에 민감한 상황에서는 주의 필요

🤔 고정 매개변수도 같은 크기의 메모리를 쓰는데, 왜 배열만 문제가 될까?

  • 힙 메모리 할당: 가변인수는 매 호출마다 힙에 새 배열 객체 생성

  • 가비지 컬렉션 부담: 임시 배열은 메서드 종료 후 배열은 즉시 가비지가 되어 GC에 부담

  • 객체 생성 오버헤드: 생성되는 배열 객체는 단순한 데이터 저장 외에도 객체 헤더를 포함 (클래스 메타데이터 포인터, 해시코드, 동기화 정보 등)

  • 메모리 단편화: 빈번한 배열 할당/해제는 힙 메모리 단편화 유발

  • CPU 캐시 효율 저하: 예측 불가능한 메모리 접근 패턴으로 캐시 효율성 감소

🛡️ 안전성 이슈

👉 가변인수는 인자가 없어도 메서드 호출이 가능하므로, 빈 배열에 대한 처리 필요

🧩 제네릭과 가변인수의 조합

⚠️ 제네릭 배열 생성 불가 문제: 실체화 불가 타입으로 배열을 만들 수 없는데, 가변인수는 내부적으로 배열 생성 ⚠️ 컴파일러 경고 발생: 타입 안전성을 보장할 수 없어 경고 발생, @SafeVarargs로 억제 가능


🧪 해결책

🛠️ 성능 최적화: 오버로딩 활용하기

자주 사용되는 인자 개수에 대해 별도 메서드를 제공하고, 나머지 경우만 가변인수 메서드로 처리

👉 자주 사용되는 케이스에 대해서는 배열 생성 비용 절약

🔒 안전한 가변인수 설계

👉 필수 매개변수를 별도로 분리하여 빈 인자 문제 해결

🛡️ 제네릭 가변인수 안전하게 사용하기

👉 제네릭 가변인수 사용 시 안전한 방법:

  1. 가변인수 배열에 아무것도 저장하지 않기

  2. 배열의 참조를 외부로 노출하지 않기

  3. @SafeVarargs 애너테이션으로 의도 명시하기


📋 가변인수 활용 예시

📝 문자열 연결


🎯 결론

📍 가변인수는 메서드에 가변 개수의 인자를 전달할 때 유용하지만, 성능과 안전성 문제에 주의해야 함

📍 매번 호출마다 배열이 생성되므로 성능에 민감한 상황에서는 오버로딩을 통해 최적화

📍 빈 배열이 생성될 수 있는 상황을 고려하여 방어적으로 코딩

📍 정말 필요한 상황에서만 가변인수를 사용하고, 남용하지 말 것

✨ "가변인수는 편리하지만, 언제나 신중하게!" 이 아이템의 핵심이다.


💭 느낀 점

💡 가변인수는 정말 편리하지만, 내부적으로 배열이 생성된다는 사실을 항상 염두에 두고 성능에 민감한 상황에서는 주의해서 사용해야 함을 이해함

💡 자주 호출되는 메서드에서 가변인수를 사용할 경우, 수많은 배열이 생성되어 성능 저하로 이어질 수 있다는 점이 인상적

💡 자바의 타입 시스템과 가변인수의 내부 동작을 제대로 이해하는 것이 성능 최적화에 있어 중요함을 느낌

Last updated