Java Varargs: Methods with Variable-Length Arguments
Varargs (short for variable arguments) is Java syntax that lets you declare a method accepting a variable number of arguments. Such methods are also called methods with variable-length arguments. The feature was introduced in Java 5 and has not changed since.
A variable-length parameter is declared with three dots after the type:
static void test(int... array) You can call such a method with any number of arguments — including none at all:
test(); // 0 arguments
test(1); // 1 argument
test(1, 2, 3); // 3 arguments
test(new int[]{1, 2, 3}); // an array works too Rules for declaring varargs
A method may have ordinary parameters alongside the variable-length one. However, the varargs parameter must come last in the parameter list, and a method can have only one of them:
static void test(double d, int... array) // valid
// static void test(int... array, double d) // compile error
// static void test(int... a, double... b) // compile error | Rule | Example | Result |
|---|---|---|
| Three dots go after the parameter type | void test(int... a) | The method accepts zero or more int values |
| The varargs parameter must be last | void test(String s, int... a) | Valid; the reverse order does not compile |
| Only one varargs parameter per method | void test(int... a, long... b) | Compile error |
| Inside the method the parameter is an array | a.length, a[0] | Every array operation is available |
| Calling with no arguments is allowed | test() | An empty array is passed, a.length == 0 |
Good to know
The entry point can be declared with varargs too: public static void main(String... args) is completely equivalent to String[] args, and the JVM will launch such a program without complaint.
Example: a method with a variable number of arguments
In the example below, the test() method is declared with a variable number of int arguments. Inside the method the parameter is used as an ordinary array. At the call site you can pass any number of values — including zero — or an existing array:
public class VarArgsExample {
static void test(int... array) {
System.out.println("Number of arguments: " + array.length);
for (int a : array) {
System.out.print(a + " ");
}
System.out.println();
}
public static void main(String[] args) {
test();
test(1);
test(1, 2);
test(new int[]{1, 3});
}
} Program output:
Number of arguments: 0
Number of arguments: 1
1
Number of arguments: 2
1 2
Number of arguments: 2
1 3 Varargs under the hood: it is just an array
Varargs is syntactic sugar over arrays. The compiler creates the array at the call site and passes it as the argument, so after compilation these two lines are indistinguishable:
test(1, 2, 3);
// the compiler turns the call above into this one:
test(new int[]{1, 2, 3}); Two practical consequences follow from that:
- inside the method you can use
length, indexing, thefor-eachloop,Arrays.toString()and everything else that works on arrays; - a call with no arguments delivers an empty array, not
null, so readingarray.lengthis always safe.
Important
Every varargs call allocates a fresh array on the heap. In ordinary code you will never notice it, but in hot paths those allocations add up. That is exactly why JDK methods such as List.of() ship dedicated overloads for 0–10 elements and fall back to the varargs version only from the eleventh argument on.
Method overloading with varargs
Methods with a variable number of arguments take part in method overloading under special rules. The compiler resolves the call in three phases and moves on to the next phase only when the current one produces no applicable candidate.
| Phase | What is considered | What is ignored | Example call |
|---|---|---|---|
| 1. Strict invocation | Exact match and primitive widening | Autoboxing, varargs | test(3) → test(int a) |
| 2. Loose invocation | Autoboxing / unboxing | Varargs | test(3) → test(Integer a) |
| 3. Variable-arity invocation | Methods with a variable number of arguments | — | test(1, 2) → test(int... a) |
Hence the golden rule: a varargs method is always considered last. If a fixed-arity method fits the call, that one wins.
When several varargs methods are applicable in phase 3, the compiler picks the most specific one — the candidate whose arguments could be handed to the other candidate without losing information. In the example below test(int... array) is more specific than test(double... array), because int widens to double but not the other way round:
public class VarArgsExample2 {
static void test(double... array) {
System.out.println("test(double... array)");
System.out.println("Number of arguments: " + array.length);
for (double a : array) {
System.out.print(a + " ");
}
System.out.println();
}
static void test(int... array) {
System.out.println("test(int... array)");
System.out.println("Number of arguments: " + array.length);
for (int a : array) {
System.out.print(a + " ");
}
System.out.println();
}
static void test(int a) {
System.out.println("test(int a)");
}
public static void main(String[] args) {
test();
test(3);
test(1.0);
test(1, 2);
}
} Program output:
test(int... array)
Number of arguments: 0
test(int a)
test(double... array)
Number of arguments: 1
1.0
test(int... array)
Number of arguments: 2
1 2 Call by call:
test()— no fixed-arity method fits, so phase 3 kicks in; of the two varargs candidates the more specifictest(int... array)is chosen;test(3)—test(int a)already matches in phase 1, so varargs are never considered;test(1.0)—doublecannot be narrowed toint, leaving onlytest(double... array);test(1, 2)— no fixed-arity method takes two arguments, sotest(int... array)wins again.
The ambiguity error
Overloading methods with variable-length arguments makes it easy to end up with an ambiguity: several candidates match equally well and none of them is more specific. This situation is caught by the compiler at build time — the code simply does not compile, so the JVM never sees it.
In the example below test is overloaded: one version takes boolean varargs, the other takes int varargs. There is no widening relationship between boolean and int, so a no-argument call test() cannot be resolved:
public class VarArgsExample3 {
static void test(boolean... array) {
System.out.println("test(boolean... array)");
System.out.println("Number of arguments: " + array.length);
for (boolean a : array) {
System.out.print(a + " ");
}
System.out.println();
}
static void test(int... array) {
System.out.println("test(int... array)");
System.out.println("Number of arguments: " + array.length);
for (int a : array) {
System.out.print(a + " ");
}
System.out.println();
}
public static void main(String[] args) {
// test(); // compile error: reference to test is ambiguous
test(3);
test(1, 2);
}
} Uncomment the test() call and javac reports:
error: reference to test is ambiguous
both method test(boolean...) and method test(int...) match There are two ways out: pass an explicit empty array of the type you want — test(new int[0]) — or drop one of the overloads and give the methods different names.
Varargs and generics: @SafeVarargs
When a varargs parameter has a generic type, the compiler warns about possible heap pollution: because of type erasure an array of List<String> is really a List[] at runtime, and anything can be stored into it.
static void unsafe(List<String>... lists) { // warning: possible heap pollution
Object[] objects = lists;
objects[0] = List.of(1, 2, 3); // the compiler does not object
String s = lists[0].get(0); // ClassCastException at runtime
} If the method only reads the elements and never writes into the array, the warning is suppressed with the @SafeVarargs annotation:
@SafeVarargs
static <T> List<T> toList(T... items) {
return new ArrayList<>(Arrays.asList(items));
} Where @SafeVarargs is allowed
The annotation may only be applied to methods that cannot be overridden: static and final methods, constructors, and since Java 9 also private methods. On a regular instance method it is a compile error, because a subclass could break the safety you promised.
Varargs in the standard library
Varargs is used all over the JDK, and several familiar methods feel like magic precisely because of it:
System.out.printf("%s = %d%n", "age", 30); // printf(String, Object...)
String s = String.format("%s-%s", "a", "b"); // format(String, Object...)
List<String> list = List.of("a", "b", "c"); // of(E...)
List<Integer> nums = Arrays.asList(1, 2, 3); // asList(T...)
int[] copy = {1, 2, 3};
System.out.println(Arrays.toString(copy)); Where developers get tripped up
- Passing
nulldirectly. Callingtest(null)ontest(String... s)does not pass "one null element" — it passesnullas the whole array, and the very nexts.lengththrowsNullPointerException. To pass a single null element, writetest((String) null). - A primitive array where an object array is expected.
Arrays.asList(new int[]{1, 2, 3})returns aList<int[]>of size 1, not a list of three numbers: for aT...parameter anint[]counts as a single object. UseInteger[]orArrays.stream(...).boxed(). - Expecting
nullon an empty call. Anif (array == null)check is pointless for normal call sites: what arrives is an empty array. - An overly greedy varargs method.
test(Object... args)matches almost any call and quietly swallows invocations you intended for another overload. - Using varargs instead of validation. If at least one argument is always required, declare
test(int first, int... rest)— then the compiler enforces the rule instead of a runtimeif.
Frequently asked questions
Can I pass an array to a varargs method?
Yes. A method test(int... array) accepts both test(1, 2, 3) and test(new int[]{1, 2, 3}) — in the first case the compiler builds the array for you, so the calls are equivalent. The reverse is not true: a method declared as test(int[] array) cannot be called as test(1, 2, 3).
What does array.length return when the method is called with no arguments?
Zero. The compiler passes an empty array rather than null, so both array.length and a for-each loop work fine. The parameter can only be null if someone passed it explicitly, for example test((int[]) null).
Why must the varargs parameter come last?
Otherwise the compiler could not tell where the variable-length list ends and the next parameters begin. The same reasoning is why a method may declare only one varargs parameter. Everything else goes before it: void log(String prefix, Object... values).
Which is faster: varargs or overloads with fixed parameters?
Fixed-parameter overloads, because varargs allocates a new array on every call. For application code the difference is negligible, but libraries and hot loops do care — that is why List.of() in the JDK has separate overloads for zero to ten elements.
Can a constructor or an abstract method use varargs?
Yes. Constructors, abstract methods, interface methods and generic methods can all declare a varargs parameter under the same rules: it must be last and there can be only one. A method that overrides a varargs method must keep the varargs form, otherwise the call sites that relied on it stop compiling.
Comments