가변 인자(Vararags)

2024. 6. 10. 16:51· Java
목차
  1. 가변 인자(vararags)
  2. 가변 인자 주의점
  3. 1. 가변 인자 성능 문제
  4. 2. 가변 인자 자체를 오버로딩 X
  5. 3. 제네릭 타입과 함께 쓸 땐 신중히

가변 인자(vararags)


가변 인자(varargs)는 메서드가 매개변수의 개수에 관계없이 동적으로 여러 인수를 받게 해주는 문법입니다. 이는 Java 5 이후로 도입된 기능으로, 메서드를 호출할 때 인수의 개수가 가변적일 때 유용합니다.

 

예를 들어 다음과 같이 매개변수의 개수가 가변적인 'print()' 메서드가 있다고 가정합니다.


            
public class Main {
public static void main(String[] args) {
print("사과");
print("사과", "바나나");
print("사과", "바나나", "포도");
}
public static void print(String s1) {
System.out.println(s1);
}
public static void print(String s1, String s2) {
System.out.print(s1 + " ");
System.out.println(s2);
}
public static void print(String s1, String s2, String s3) {
System.out.print(s1 + " ");
System.out.print(s2 + " ");
System.out.println(s3);
}
}

위처럼 여러 매개변수를 받아야 할 때, 메서드 오버로딩(overloading)은 대표적인 방법이지만 번거롭고 비효율적입니다.

가변 인자는 이를 해결하며, 매개변수를 배열로 처리하여 동적으로 여러 개의 인수를 받을 수 있습니다.


            
public class Main {
public static void main(String[] args) {
print("사과");
print("사과", "바나나");
print("사과", "바나나", "포도");
}
public static void print(String... args) {
Arrays.stream(args)
.forEach(s -> System.out.print(s + " "));
System.out.println();
}
}

만일 매개변수가 가변 인자 외에 다른 매개변수를 받는다면, 반드시 매개변수 중 가장 마지막에 위치하도록 정의해야 합니다. 마지막에 선언하지 않을 경우, Java는 가변 인자인지 아닌지를 구별할 방법이 없기 때문입니다.


            
// 컴파일 에러 발생!! - java: varargs parameter must be the last parameter
public void print(String... args, String name) { ... }

 

 

가변 인자 주의점


1. 가변 인자 성능 문제

가변 인자는 내부적으로 배열을 이용하기 때문에 메서드를 호출하는 시점마다 배열을 새로 생성하는 단점을 가지고 있습니다. 이러한 이유로 성능이 민감한 상황에서는 단점이 될 수 있습니다. 그럼에도 사용해야 하는 상황이라면 다음과 같은 방식을 생각해 볼 수 있습니다.


            
class Print {
public void print(String s1) { ... }
public void print(String s1, String s2) { ... }
public void print(String s1, String s2, String s3) { ... }
public void print(String s1, String... args) { ... }
}
더보기
💡 EnumSet 클래스에서 위와 같은 기법을 통해 열거 타입의 집합 생성 비용을 최적화하고 있습니다.

 

🤔 매개변수 타입을 배열로 선언하면 가변 인자와 똑같은 것이 아닌가?


            
public class Main {
public static void main(String[] args) {
print(); // 컴파일 에러 - 인자가 필요
print(null); // 인자로 null을 지정
print(new String[0]); // 인자로 배열을 지정
}
public static void print(String[] str) {
Arrays.stream(str)
.forEach(s -> System.out.print(s + " "));
System.out.println();
}
}

매개변수의 타입을 배열로 선언하면, 반드시 인자를 넣어줘야 하기 때문에 사용하지 않더라도 null이나 0인 배열을 인자로 넣어줘야 하는 불편함이 있습니다.

 

2. 가변 인자 자체를 오버로딩 X


            
class Printer {
public void print(String c, String... args) {
System.out.println("첫번째 메서드");
}
public void print(String... args) {
System.out.println("두번째 메서드");
}
}
public class Main {
public static void main(String[] args) {
Printer p = new Printer();
p.print("-", "1", "2", "3"); // 컴파일러가 첫번째와 두번째 메서드 중 누굴 실행시켜야 할지 몰라 컴파일 오류가 발생된다.
}
}

가변 인자를 오버로딩 할 경우 컴파일러는 어떤 메서드를 사용해야 할지 구분할 수 없기 때문에 컴파일 오류가 발생됩니다.

가변 인자 오버로딩 컴파일 에러

 

3. 제네릭 타입과 함께 쓸 땐 신중히

가변 인자는 파라미터를 동적으로 여러 인수를 받을 수 있도록 조절해 주지만 구현 방식에 있어 허점이 존재합니다. 가변 인자를 받는 메서드를 호출하게 되면 해당 시점에 가변 인자를 담을 배열을 새로 생성하게 되는데, 이때 제네릭 타입 파라미터와 같은 실체화 불가 타입은 런타임 시 타입 정보가 소거되기 때문에, 가변 인자에 제네릭이나 매개변수화 타입이 포함되고 이를 노출할 경우 문제가 될 수 있습니다.

 

가변인자에 제네릭 타입을 사용하면 컴파일러 경고가 발생할 수 있으며, 이는 가변인자 배열이 Object 배열로 변환되기 때문에 발생합니다. 즉, 다음과 같은 상황에서 가변인자는 어떠한 타입도 받을 수 있는 최상위 타입 Object 배열로 선언되며, 이를 다운 캐스팅하여 String 배열로 받을 수 없습니다.


            
class Printer {
public <T> T[] toArray(T... args) {
return args; // 타입을 특정할 수 없기 때문에 어떤 타입이 들어와도 문제없는 Object 배열로 반환된다.
}
public <T> T[] pick(T a, T b, T c) {
T[] arr = toArray(a, b, c); // Object 배열 반환
return arr;
}
}
public class Main {
public static void main(String[] args) {
Printer p = new Printer();
// 제네릭 메서드에 String 타입으로 전달하였으나, String 으로 반환 받을 수 없다.
String[] s = p.pick("1","2","3"); // Object 배열을 String 배열로 다운캐스팅하여 문제 발생!
}
}

이는 제네릭 배열이 타입 안전성을 보장할 수 없기 때문입니다. 대신, 다음과 같이 List를 사용하는 것이 좋습니다. 또한 제네릭 타입은 힙오염이 발생되지 않도록 주의해야 합니다.


            
class Printer {
public <T> List<T> toArray(T... args) {
List<T> list = new ArrayList<>();
for (T arg : args) {
list.add(arg);
}
return list;
}
public <T> List<T> pick(T a, T b, T c) {
List<T> arr = toArray(a, b, c);
return arr;
}
}

 

'Java' 카테고리의 다른 글

일급 컬렉션(First Class Collection)  (1) 2024.07.16
for loop vs stream forEach  (0) 2024.03.04
직렬화(Serialize)란?  (2) 2024.01.22
  1. 가변 인자(vararags)
  2. 가변 인자 주의점
  3. 1. 가변 인자 성능 문제
  4. 2. 가변 인자 자체를 오버로딩 X
  5. 3. 제네릭 타입과 함께 쓸 땐 신중히
'Java' 카테고리의 다른 글
  • 일급 컬렉션(First Class Collection)
  • for loop vs stream forEach
  • 직렬화(Serialize)란?
Hui._.
Hui._.
High hope
Hui._.
개발 일기
Hui._.
전체
오늘
어제
  • 분류 전체보기 (57)
    • Java (4)
    • Spring (26)
      • Spring Framework (6)
      • Spring Security (9)
      • JPA (11)
    • CS (2)
    • 알고리즘 (19)
      • 문제풀이 (16)
      • 자료구조 (3)
    • ETC (3)
    • Project (3)
      • Trouble Shooting (3)

블로그 메뉴

  • 홈
  • 글쓰기
  • 설정

공지사항

인기 글

태그

  • jpa
  • 인터페이스
  • Spring Boot 3.2.3
  • dynamic programming
  • jwt
  • 스프링 부트3
  • Oauth 2.0
  • 알고리즘
  • java
  • 원칙
  • SOLID
  • 프로그래머스
  • 추상화
  • 최소공배수
  • 최대공약수
  • 스프링 시큐리티6.1
  • 코딩테스트
  • 호제법
  • 분리
  • 엔티티
  • Spring Security + JWT + OAuth 2.0
  • 유클리드
  • 회원 기능
  • Spring Security
  • HashMap
  • oauth
  • 매핑
  • 지연
  • persist
  • Spring Security 6.1

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
Hui._.
가변 인자(Vararags)
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.