Varargs
Varargs (short for variable-length arguments) let you write a method that accepts any number of arguments of the same type — zero, one, three, or a hundred — without forcing the caller to build an array manually. Introduced in Java 5, varargs make APIs like System.out.printf and many collection helpers possible.
The Problem Varargs Solve
Before Java 5, if you wanted a method to accept an unknown number of integers, you had two choices: overload the method for each count, or ask the caller to pass an array. Both approaches are awkward.
// Before varargs — caller must build an array
int sum(int[] numbers) {
int total = 0;
for (int n : numbers) total += n;
return total;
}
// Calling it is clunky
sum(new int[]{1, 2, 3});
Varargs replace this boilerplate with clean, natural call sites.
Varargs Syntax
Add ... between the type and the parameter name. That’s it.
public class MathUtils {
// accepts zero or more ints
public static int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
public static void main(String[] args) {
System.out.println(sum()); // 0
System.out.println(sum(5)); // 5
System.out.println(sum(1, 2, 3)); // 6
System.out.println(sum(10, 20, 30, 40)); // 100
}
}
Output:
0
5
6
100
Inside the method body, numbers is simply an int[]. You can loop over it with a for-each loop, check its .length, use Arrays.sort() — anything you would do with a regular array.
Mixing Varargs with Other Parameters
A varargs parameter can coexist with regular parameters, but it must be last.
public class Greeter {
public static String greet(String prefix, String... names) {
StringBuilder sb = new StringBuilder();
for (String name : names) {
sb.append(prefix).append(name).append("\n");
}
return sb.toString();
}
public static void main(String[] args) {
System.out.print(greet("Hello, ", "Alice", "Bob", "Charlie"));
}
}
Output:
Hello, Alice
Hello, Bob
Hello, Charlie
Warning: A method can have at most one varargs parameter, and it must be the last parameter in the signature.
void foo(int... a, String... b)is a compile error.
Passing an Array to a Varargs Method
Because varargs is an array under the hood, you can pass an existing array directly — no extra syntax needed.
int[] data = {4, 5, 6};
System.out.println(sum(data)); // 15
This makes varargs backward-compatible with code that already builds arrays.
Varargs and Overloading
You can overload a varargs method with a fixed-arity version. The compiler always prefers the most specific match.
public class Printer {
static void print(String s) {
System.out.println("Single: " + s);
}
static void print(String... items) {
System.out.println("Varargs: " + items.length + " item(s)");
}
public static void main(String[] args) {
print("hi"); // Single: hi (more specific)
print("a", "b"); // Varargs: 2 item(s)
print(); // Varargs: 0 item(s)
}
}
Output:
Single: hi
Varargs: 2 item(s)
Varargs: 0 item(s)
Tip: To avoid ambiguous overloads, keep varargs methods simple and don’t create two varargs overloads that differ only in type (e.g.,
Object...vsString...) — the compiler may complain at call sites.
Varargs with Generics — the Heap Pollution Warning
When you mix varargs with generics, the compiler emits an unchecked warning:
@SafeVarargs
public static <T> java.util.List<T> listOf(T... items) {
return java.util.Arrays.asList(items);
}
The warning exists because passing a generic varargs array can cause heap pollution — the array’s actual runtime type may not match its declared type. If your method does nothing dangerous with the array (just reads from it), suppress the warning with @SafeVarargs on a final, static, or private method.
Note:
@SafeVarargswas introduced in Java 7. In Java 9+, it can also be applied to private instance methods.
Real-World Example: printf-style Logging
The JDK’s own String.format and System.out.printf use varargs. Here’s a tiny logger that does the same:
public class Logger {
public static void log(String level, String template, Object... args) {
String message = String.format(template, args);
System.out.println("[" + level + "] " + message);
}
public static void main(String[] args) {
log("INFO", "Server started on port %d", 8080);
log("ERROR", "Failed to connect to %s after %d retries", "db.host", 3);
}
}
Output:
[INFO] Server started on port 8080
[ERROR] Failed to connect to db.host after 3 retries
Under the Hood
At the bytecode level, varargs is purely syntactic sugar. The compiler rewrites every varargs call into an array creation and pass:
// Source code
sum(1, 2, 3);
// Compiled equivalent
sum(new int[]{1, 2, 3});
This means:
- Every call site allocates a new array on the heap (even a zero-argument call creates an empty
int[0]). - For hot inner loops, this allocation pressure matters. If a varargs method is called millions of times per second, consider providing a fixed-arity overload for the common cases (0–4 arguments) — exactly what the JDK does in
EnumSet.of(). - The JIT compiler can sometimes eliminate the allocation via escape analysis when the array does not outlive the call, but this is not guaranteed.
You can confirm the rewriting yourself with the javap tool:
javap -c MathUtils
Look for newarray int immediately before the invokestatic instruction that calls sum.
Common Pitfalls
| Pitfall | What happens | Fix |
|---|---|---|
| Varargs not last | Compile error | Move varargs param to end |
| Two varargs overloads | Ambiguous call site | Refactor to avoid ambiguity |
null passed to Object... | NullPointerException when iterating | Guard with a null check |
| Unchecked generic warning | Potential heap pollution | Add @SafeVarargs if safe |
The null Trap
public static void printAll(String... items) {
for (String s : items) { // NPE if items itself is null
System.out.println(s);
}
}
// Dangerous call:
printAll(null); // passes null AS the array, not an element
The fix is a null guard at the top of the method:
if (items == null) return;
Quick Rules Cheat-Sheet
type... namedeclares a varargs parameter.- It must be the last (and only varargs) parameter.
- Inside the method, it is a regular array.
- You may pass an existing array directly.
- Each call site allocates a new array — avoid in tight loops.
- Use
@SafeVarargson safe generic varargs methods to suppress the compiler warning.
Related Topics
- Autoboxing & Unboxing — how primitives and wrappers interact, including inside varargs calls
- Generics — understanding heap pollution and type erasure that make
@SafeVarargsnecessary - Methods — the full picture of method parameters, return types, and overloading
- Method Overloading — how the compiler picks among overloaded methods when varargs is involved
- Arrays — varargs parameters are arrays; this page covers arrays in depth
- Annotations —
@SafeVarargsand other built-in annotations explained