가변인자란?
자바의 가변인자는 메서드의 파라미터 개수를 유동적으로 관리할 수 있게 해주는 기능입니다.
이를 통해 개발자는 특정 메서드에 다양한 수의 인자를 전달할 수 있으며, 이는 동일한 기능의 메서드를 여러 개 정의하지 않고도 다양한 인자 조합을 처리할 수 있게 됩니다.
가변인자 예제
public static void main(String[] args) {
// 가변 인자를 사용하여 3개의 정수를 전달합니다.
System.out.println("sum(1, 2, 3) = " + sum(1, 2, 3));
// 가변 인자를 사용하여 4개의 정수를 전달합니다.
System.out.println("sum(1, 2, 3, 4) = " + sum(1, 2, 3, 4));
// 가변 인자를 사용하여 5개의 정수를 전달합니다.
System.out.println("sum(1, 2, 3, 4, 5) = " + sum(1, 2, 3, 4, 5));
}
// 가변 인자를 사용하여 정수들의 합을 계산합니다.
public static int sum(int... numbers) {
int sum = 0;
for (int number : numbers) {
sum += number;
}
return sum;
}
위의 코드에서 메서드는 0개 이상의 int 값을 파라미터로 받을 수 있으며, 이는 내부적으로 배열로 처리됩니다.
가변인자 메서드 타입 안전성 문제
다음과 같은 경우에 가변인자의 타입 안전성에 문제가 생깁니다.
public static <T> T[] unsafe(T... elements) {
return elements;
}
public static <T> T[] broken(T seed) {
T[] plant = unsafe(seed, seed, seed);
return plant;
}
public static void plant() {
String[] plants = broken("seed");
}
1. unsafe 메서드
unsafe 메서드는 제네릭 가변인자 배열을 반환하고 있습니다.
가변인자 메서드에서는 타입 안전성이 보장되지 않을 수 있기 때문에, 배열을 그대로 반환하는 것은 매우 위험합니다.
가변인자 배열은 내부적으로 Object[] 배열로 변환되므로, 호출 시 타입 안정성이 깨질 가능성이 높습니다.
2. broken 메서드
broken 메서드는 unsafe 메서드를 호출하여 배열을 반환합니다.
unsafe(seed, seed, seed)를 통해 생성된 배열은 실제로 Object[] 타입입니다.
따라서 T[]로 반환한다고 해도 컴파일러는 Object[]를 T[]로 인식하지 못해 타입 안정성을 보장하지 못합니다.
3. plant 메서드
plant 메서드는 broken 메서드를 호출하면서 String 타입의 배열을 기대하지만, 실제로는 Object[] 배열을 반환받기 때문에 ClassCastException이 발생합니다.
Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.String; ([Ljava.lang.Object; and [Ljava.lang.String; are in module java.base of loader 'bootstrap')
@SafeVarargs란?
@SafeVarargs는 자바에서 가변인자를 안전하게 사용할 수 있도록 돕는 어노테이션입니다.
주로 제네릭 가변인자 메서드에서 경고를 억제하기 위해 사용되며, 7 버전에서 도입되었습니다.
이 어노테이션을 통해 제네릭 가변인자 사용 시 발생할 수 있는 타입 안전성 경고를 방지할 수 있습니다.
가변인자 메서드는 배열을 사용하기 때문에, 제네릭 가변인자는 타입 소거(Type Erasure) 과정에서 타입 안전성이 완벽하게 보장되지 않아 컴파일러가 경고를 발생시킬 수 있습니다.
이러한 경고를 억제하는 것이 @SafeVarargs의 역할입니다.
@SafeVarargs 사용 시 주의사항
- @SafeVarargs는 타입 안전한 가변인자 메서드에만 적용해야 합니다.
- 제네릭 가변인자를 반환하는 경우, 특히 배열을 그대로 반환하는 방식은 위험할 수 있으므로 지양해야 합니다.
- 안전한 메서드를 작성하려면 타입 캐스팅 없이도 안전하게 작동하는 방법을 사용하는 것이 좋으며, 가능하다면 배열 대신 리스트나 컬렉션을 사용하는 것이 안전성을 높이는 방법입니다.
@SafeVarargs는 컴파일러 경고를 억제해주지만, 내부적으로 타입 안전성을 보장하지 못할 경우 런타임에 예외가 발생할 수 있으므로 주의가 필요합니다.
참고
https://www.baeldung.com/java-safevarargs