Menu

Java 가변 인자(Varargs): 임의 개수의 인자를 받는 메서드

Java의 가변 인자(...)가 메서드로 하여금 임의 개수의 인자를 받게 하는 방법, 그것이 배열이 되는 원리, 가변 인자는 마지막에 하나만이라는 규칙, 그리고 빈 호출과 모호성의 함정.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

하나의 메서드, 임의 개수의 인자

메서드에 인자가 몇 개 필요한지 미리 알 수 없을 때가 있습니다. 어떤 순간에는 max(3, 9)가, 다음 순간에는 max(1, 7, 4, 2, 8)가 필요할 수 있습니다. 가변 인자가 없다면 개수마다 오버로드를 작성하거나 호출하는 쪽에 배열을 만들도록 강제해야 합니다. 가변 인자(variable arguments의 줄임말)는 하나의 메서드가 같은 타입의 값을 0개, 1개, 또는 여러 개 받을 수 있게 합니다.

매개변수 타입 뒤에 점 세 개(...)를 붙여 선언합니다:

호출하는 쪽은 흩어진 값들을 전달하고, 메서드 안에서 numbers는 진짜 배열입니다. 이것이 핵심 전부입니다. 컴파일러가 인자들을 모아 배열로 묶어주는 것이죠.

가변 인자는 그저 배열일 뿐

런타임에 마법은 없습니다. int... numbers로 선언된 매개변수는 메서드 안에 들어오면 정확히 int[]입니다. 그 length를 확인하고, 인덱스로 접근하고, 다른 배열처럼 순회할 수 있습니다.

진짜 배열이기 때문에, 이미 가지고 있는 배열을 가변 인자 메서드에 넘길 수도 있습니다 - 두 형태 모두 받습니다:

규칙: 마지막에, 그리고 하나만

두 가지 규칙이 가변 인자를 모호하지 않게 유지하며, 컴파일러는 둘 다 강제합니다:

  1. 가변 인자 매개변수는 마지막이어야 한다. 일반 매개변수가 먼저 오고, 그다음 ... 매개변수가 남은 것을 모두 빨아들입니다.
  2. 메서드는 가변 인자 매개변수를 최대 하나만 가질 수 있다. 두 개라면 하나가 어디서 끝나고 다음이 어디서 시작하는지 구분할 수 없게 됩니다.

이것은 컴파일됩니다. 하지만 가변 인자를 앞에 두면 - static void log(Object... values, String level) - 컴파일 오류입니다: varargs parameter must be the last. 필수 매개변수 level은 항상 ...보다 앞에 옵니다.

이미 가변 인자를 쓰고 있는 곳

사실 줄곧 가변 인자 메서드를 호출해 왔습니다. System.out.printfString.format이 대표적인 예로, 두 번째 매개변수가 Object... args입니다:

List.of(...), Arrays.asList(...), 그리고 많은 빌더 스타일 API도 가변 인자입니다. 그래서 직접 배열로 감싸지 않고도 임의 개수의 요소를 전달할 수 있는 것입니다.

가변 인자와 오버로딩

가변 인자와 오버로딩은 한 가지 놀라운 방식으로 상호작용합니다. 일반 오버로드와 가변 인자 오버로드가 둘 다 어떤 호출에 일치할 수 있을 때, Java는 더 구체적인 쪽을 선호합니다 - 고정 인자 개수를 가진 메서드가 이깁니다:

pick(1, 2)는 둘 다에 일치하지만, Java는 더 가까운 적합이기 때문에 가변 인자가 아닌 오버로드를 선택합니다. 가변 인자 메서드는 고정 인자 개수의 오버로드가 하나도 일치하지 않을 때만 실행됩니다. 보통은 이것이 원하는 동작이지만, 가변 인자 오버로드를 추가하면 어떤 호출이 어느 메서드로 결정되는지가 조용히 바뀔 수 있다는 뜻이기도 합니다.

빈 호출과 null 호출을 조심하라

두 가지 함정이 사람들을 무는데요. 첫째, 가변 인자 메서드를 인자 없이 호출하면 메서드에 null이 아니라 빈 배열이 전달됩니다 - 따라서 numbers.length0이고 반복문은 아무것도 하지 않습니다. 인자가 없는 경우에 null 검사는 필요하지 않습니다:

둘째, 리터럴 null을 전달하는 것은 모호하고 위험합니다. Java는 count(null)을 "null 요소 하나"가 아니라 "가변 인자 배열 전체가 null"로 해석할 수 있으며, 그 length를 읽을 때 NullPointerException을 던집니다. 정말로 null 요소 하나를 의도한다면 count((Object) null)처럼 명시하세요.

다음: 클래스

가변 인자는 메서드 매개변수로 할 수 있는 것을 완성합니다 - 오버로딩 위에 유연한 인자 목록을 얹은 것이죠. 지금까지 메서드들은 static을 붙인 채 클래스 안에 느슨하게 놓여 있었습니다. 다음에는 클래스가 실제로 어떻게 동작하는지 보게 됩니다. 데이터(필드)와 동작(메서드)을 여러분만의 타입으로 묶는 것, 그것이 객체 지향 Java의 핵심입니다.

자주 묻는 질문

Java에서 가변 인자(varargs)란 무엇인가요?

가변 인자(varargs)를 사용하면 메서드가 같은 타입의 인자를 임의 개수만큼 받을 수 있습니다. 타입 뒤에 ...을 붙여 선언합니다: int sum(int... numbers). 호출하는 쪽은 0개, 1개, 또는 여러 개의 값을 전달할 수 있고, 메서드 안에서 numbers는 평범한 배열입니다. 이것은 문법적 설탕으로, Java가 흩어진 인자들을 알아서 배열로 모아줍니다.

Java 메서드는 가변 인자 매개변수를 두 개 이상 가질 수 있나요?

아니요. 메서드는 가변 인자 매개변수를 최대 하나만 가질 수 있고, 그것은 매개변수 목록의 마지막이어야 합니다 - 예를 들면 void log(String level, Object... values)입니다. 만약 마지막이 아니라면 Java는 가변 길이 부분이 어디서 끝나고 다음 매개변수가 어디서 시작하는지 알 수 없습니다. 일반 매개변수는 항상 ... 매개변수보다 앞에 옵니다.

Java에서 가변 인자와 배열 매개변수의 차이는 무엇인가요?

런타임에는 거의 같습니다 - 가변 인자 매개변수 int...은 메서드 안에서 int[]입니다. 차이는 호출 지점에 있습니다. 가변 인자로는 sum(1, 2, 3)(흩어진 값들)처럼 쓰지만, 명시적인 배열 매개변수로는 배열을 만들어 전달해야 합니다: sum(new int[]{1, 2, 3}). 가변 인자 메서드는 배열을 직접 전달해도 받으므로 더 유연한 선택입니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기