Java Variable Arguments (varargs)
Java Variable Arguments (varargs)
Before varargs existed in Java 5, writing a method that accepted a flexible number of arguments meant either creating multiple overloads for one, two, three, four arguments, or forcing the caller to pack everything into an array first. Both approaches were clumsy. Varargs solved this.
A varargs parameter lets a method accept any number of arguments of the same type — zero, one, five, or a hundred — without the caller needing to create an array or the developer needing to write multiple overloads. The JVM creates the array behind the scenes.
What Are varargs?
varargs (variable arguments) is a special parameter syntax that allows a method to accept zero or more arguments of the same type. It is declared using three dots (...) after the type.
// This method accepts any number of int arguments
public int sum(int... numbers) { ... }
// All of these are valid calls:
sum() // zero arguments
sum(10) // one argument
sum(10, 20) // two arguments
sum(10, 20, 30, 40) // four arguments
int[] arr = {1, 2, 3};
sum(arr) // passing an existing arrayInside the method, numbers is treated as a plain int[]. The JVM collects whatever arguments are passed into an array before calling the method.
What the JVM does:
Caller writes: sum(10, 20, 30)
|
v
JVM creates: int[] numbers = {10, 20, 30}
|
v
Method receives: sum(int[] numbers) — works like a normal array
Basic Syntax and Usage
// File: VarargsBasics.java
public class VarargsBasics {
// Accepts zero or more int values
public int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
// Accepts zero or more Strings
public void printAll(String... messages) {
System.out.println("Total messages: " + messages.length);
for (String msg : messages) {
System.out.println(" " + msg);
}
}
// varargs combined with regular parameters
// Regular parameter MUST come before the varargs parameter
public void log(String level, String... messages) {
for (String msg : messages) {
System.out.println("[" + level + "] " + msg);
}
}
public static void main(String[] args) {
VarargsBasics vb = new VarargsBasics();
// sum — different number of arguments
System.out.println("sum() = " + vb.sum());
System.out.println("sum(5) = " + vb.sum(5));
System.out.println("sum(1,2,3) = " + vb.sum(1, 2, 3));
System.out.println("sum(10,20,30,40)= " + vb.sum(10, 20, 30, 40));
// Existing array also works directly
int[] marks = {85, 90, 78, 95, 88};
System.out.println("sum(array) = " + vb.sum(marks));
System.out.println();
vb.printAll(); // zero arguments — messages.length is 0
System.out.println();
vb.printAll("Server started", "Database connected");
System.out.println();
vb.log("INFO", "Server started", "Listening on port 8080");
vb.log("ERROR", "Connection refused");
}
}Output:
sum() = 0
sum(5) = 5
sum(1,2,3) = 6
sum(10,20,30,40)= 100
sum(array) = 436
Total messages: 0
Total messages: 2
Server started
Database connected
[INFO] Server started
[INFO] Listening on port 8080
[ERROR] Connection refused
When no arguments are passed, numbers.length is 0 and the loop body never executes — the method returns 0 cleanly. The caller never needs to create an array or check whether to pass null.
varargs Rules — What You Must Know
Rule 1 — Only ONE varargs parameter per method.
VALID: void method(int... nums)
VALID: void method(String s, int... nums)
INVALID: void method(int... nums, String... strs) ← compile error
Rule 2 — varargs must be the LAST parameter.
VALID: void log(String level, String... messages)
INVALID: void log(String... messages, String level) ← compile error
Rule 3 — The varargs parameter is a regular array inside the method.
public void show(int... nums) {
int[] arr = nums; // valid — nums IS an int[]
nums.length; // valid
nums[0]; // valid — but check length first!
}
Rule 4 — An existing array can be passed directly.
int[] values = {1, 2, 3};
sum(values); // valid — array passed as varargs
Rule 5 — null can be passed but causes NullPointerException when iterated.
sum(null); // compiles, but nums is null inside — NPE if you access nums.length
varargs vs Array Parameter — Comparison Table
| Aspect | int... numbers (varargs) | int[] numbers (array) |
|---|---|---|
| Caller syntax | method(1, 2, 3) — no array needed | method(new int[]{1,2,3}) — array required |
| Zero arguments | method() — valid | method(new int[]{}) or method(null) |
| Passing existing array | method(arr) — works directly | method(arr) — works directly |
| Inside the method | Treated as a regular array | Treated as a regular array |
| Position in parameter list | Must be last | Any position |
| Multiple varargs in one method | Not allowed — only one | Multiple array params allowed |
| Overloading | Compiler prefers exact match over varargs | Normal overload resolution |
| JVM | Creates array from supplied arguments | Array passed directly — no creation |
| Readability at call site | Clean — sum(1, 2, 3) | Verbose — sum(new int[]{1, 2, 3}) |
| When to use | When the number of arguments genuinely varies | When you always have a pre-existing array |
varargs and Overloading — How the Compiler Chooses
The compiler treats varargs as a last-resort match. If an exact-match overload exists, it is always preferred over the varargs version.
// File: VarargsOverloadDemo.java
public class VarargsOverloadDemo {
// Exact match — two ints
public static void display(int a, int b) {
System.out.println("Two-int overload: " + a + ", " + b);
}
// Exact match — one int
public static void display(int a) {
System.out.println("One-int overload: " + a);
}
// varargs — last resort
public static void display(int... nums) {
System.out.println("varargs overload, count=" + nums.length);
for (int n : nums) System.out.print(n + " ");
if (nums.length > 0) System.out.println();
}
public static void main(String[] args) {
display(5); // exact match — one-int overload
display(5, 10); // exact match — two-int overload
display(5, 10, 15); // no exact match — varargs
display(); // no exact match — varargs with zero elements
display(1, 2, 3, 4); // no exact match — varargs
}
}Output:
One-int overload: 5
Two-int overload: 5, 10
varargs overload, count=3
5 10 15
varargs overload, count=0
varargs overload, count=4
1 2 3 4
display(5) and display(5, 10) both have exact-match overloads — they never touch the varargs version. Only calls with three or more arguments, or zero arguments, fall through to the varargs overload.
varargs With Different Types
Varargs works with any type — including String, custom classes, and Object for mixed types.
// File: VarargsTypesDemo.java
public class VarargsTypesDemo {
// String varargs
public static String joinWith(String separator, String... parts) {
if (parts.length == 0) return "";
StringBuilder sb = new StringBuilder(parts[0]);
for (int i = 1; i < parts.length; i++) {
sb.append(separator).append(parts[i]);
}
return sb.toString();
}
// double varargs — calculates average
public static double average(double... values) {
if (values.length == 0) return 0.0;
double sum = 0;
for (double v : values) sum += v;
return sum / values.length;
}
// Object varargs — accepts anything
public static void printDetails(String label, Object... attributes) {
System.out.print(label + ": ");
for (int i = 0; i < attributes.length; i++) {
System.out.print(attributes[i]);
if (i < attributes.length - 1) System.out.print(" | ");
}
System.out.println();
}
public static void main(String[] args) {
// String varargs
System.out.println(joinWith(", ", "Priya", "Rohan", "Sneha", "Karan"));
System.out.println(joinWith(" / ", "Mumbai", "Delhi", "Bengaluru"));
System.out.println(joinWith("-", "2024", "01", "15"));
System.out.println();
// double varargs
System.out.printf("Avg marks : %.2f%n", average(85.0, 90.0, 78.0, 95.0));
System.out.printf("Avg single : %.2f%n", average(100.0));
System.out.printf("Avg empty : %.2f%n", average());
System.out.println();
// Object varargs — mix of types
printDetails("Employee", "EMP-042", "Priya Mehta", "Engineering", 85000);
printDetails("Product", "SKU-101", "Laptop", "Electronics", 45999.0, true);
}
}Output:
Priya, Rohan, Sneha, Karan
Mumbai / Delhi / Bengaluru
2024-01-15
Avg marks : 87.00
Avg single : 100.00
Avg empty : 0.00
Employee: EMP-042 | Priya Mehta | Engineering | 85000
Product: SKU-101 | Laptop | Electronics | 45999.0 | true
Object... attributes accepts any mix of types — the JVM boxes primitives automatically. This is exactly how String.format, printf, and logging frameworks like Log4j accept format arguments.
Basic Example
A small findMax method shows varargs combined with a required regular parameter, working with however many extra values the caller supplies.
public class Main {
static int findMax(int first, int... rest) {
int max = first;
for (int n : rest) {
if (n > max) {
max = n;
}
}
return max;
}
public static void main(String[] args) {
System.out.println(findMax(5));
System.out.println(findMax(5, 12, 3, 9));
}
}Output
5 12
- ›
firstis a regular required parameter, andrestis the varargs parameter — Java requires the varargs parameter to come last. - ›
findMax(5)passes no extra arguments, sorestbecomes an emptyint[], and the loop body never runs —maxstays5. - ›
findMax(5, 12, 3, 9)packs12, 3, 9intorestbehind the scenes, and the loop checks each one against the running maximum. - ›Inside the method,
restbehaves exactly like a normalint[]— varargs is just a convenient way to let the caller skip building that array explicitly.
Best Practices
Always handle the zero-argument case explicitly. When scores.length == 0 is valid input, handle it — print "no items", return 0, return an empty list. Do not assume the varargs array always has elements. Do not access arr[0] without checking arr.length > 0 first.
Use varargs when the argument count genuinely varies at the call site. If every caller always passes exactly two or three values, write two or three explicit parameters — the call site is clearer and mistakes are caught at compile time. Varargs is for genuine variability, not for avoiding the effort of typing parameter names.
Never store the varargs array reference outside the method. The JVM creates a new array for each call. Storing it and relying on it after the method returns is fragile — the caller might not expect the array to be held by anything.
Prefer explicit parameters for the first one or two required arguments. Write log(String level, String... messages) rather than log(String... args) when level is always required. The method signature communicates what is mandatory and what is optional, and the compiler enforces the mandatory part.
Common Mistakes
Mistake 1 — Accessing varargs Array Without Length Check
public static double firstValue(double... values) {
return values[0]; // ArrayIndexOutOfBoundsException if called with no arguments
}
firstValue(); // caller passes nothing — values is empty — crash
firstValue(10.0); // works fine
// Fix — check length first
public static double firstValue(double... values) {
if (values.length == 0) return 0.0;
return values[0];
}Mistake 2 — Passing null Causes NullPointerException
public static void printAll(String... messages) {
for (String msg : messages) { // NullPointerException here
System.out.println(msg);
}
}
printAll(null); // null is assigned to messages itself — not a null element inside
// messages == null — iterating it with for-each throws NullPointerExceptionException in thread "main" java.lang.NullPointerException
When null is passed to a varargs method, Java interprets it as setting the entire array to null — not as an array containing one null element. Guard against it explicitly: if (messages == null) return; or use Objects.requireNonNull.
Mistake 3 — Ambiguous Overload With varargs
public static void show(int... nums) { System.out.println("int varargs"); }
public static void show(Integer... nums) { System.out.println("Integer varargs"); }
show(1, 2, 3); // Compile error — ambiguous:
// 1, 2, 3 could match int... or Integer... (autoboxing)Compile error: reference to show is ambiguous
Defining two varargs overloads where one is the autoboxed version of the other creates an ambiguity the compiler cannot resolve. Avoid having both int... and Integer... overloads for the same method name.
Mistake 4 — Putting varargs Before Other Parameters
// Compile error — varargs must be the last parameter
public static void register(String... names, String city) { } // error
// Fix
public static void register(String city, String... names) { } // correctCompile error: varargs parameter must be the last parameter
Interview Questions
Q1. What are varargs in Java and how do they work internally?
varargs (variable arguments) allow a method to accept any number of arguments of the same type using the ... syntax. The JVM collects the supplied arguments into an array before calling the method — inside the method, the varargs parameter behaves as a regular array. A method can have at most one varargs parameter and it must be the last parameter in the list. Callers can pass individual values, an existing array, or nothing at all.
Q2. What are the rules for using varargs in Java?
A method can have only one varargs parameter. The varargs parameter must be the last parameter in the method signature — placing it before any other parameter causes a compile error. Inside the method, the varargs parameter is a regular array and can be iterated, passed to other methods, or checked with .length. An existing array can be passed directly as a varargs argument. When null is passed, the array reference itself is null — not an array containing null.
Q3. What is the difference between varargs and an array parameter?
A varargs parameter allows the caller to pass individual values directly — method(1, 2, 3) — while an array parameter forces the caller to construct an array first — method(new int[]{1, 2, 3}). Inside the method, both are treated identically as arrays. The key practical difference is call-site readability: varargs reads naturally for variable-count inputs, while an array parameter is better when the caller always has a pre-built collection.
Q4. How does the compiler resolve overloads when a varargs method and a fixed-parameter method both match?
The compiler applies a resolution order: exact match first, then widening, then autoboxing, and varargs last. If a fixed-parameter method matches the argument types exactly, it is always preferred over the varargs version. Varargs is the last resort — used only when no fixed-parameter method matches. This is why display(5, 10) calls the display(int, int) overload rather than display(int... nums) when both exist.
Q5. What happens when you pass null to a varargs method?
Java interprets null passed to a varargs parameter as setting the entire array reference to null, not as an array containing a single null element. Iterating over the parameter with a for-each loop then throws NullPointerException because the array itself is null. To handle this safely, check if (parameter == null) return; at the top of the method, or use Objects.requireNonNull.
Q6. Where does Java use varargs in its own standard library?
The most widely used varargs in the standard library are System.out.printf(String format, Object... args) and String.format(String format, Object... args) — which accept format strings and any number of values. Arrays.asList(T... elements) creates a list from any number of elements. Collections.addAll(Collection c, T... elements) adds multiple elements to a collection. Logging frameworks like SLF4J and Log4j use log.info(String message, Object... args) for parameterised logging.
FAQs
Can varargs be used with generics?
Yes — <T> with T... is valid. public static <T> List<T> listOf(T... elements) is a generic varargs method. However, mixing generics with varargs generates an unchecked warning — "Possible heap pollution via varargs parameter" — because the JVM cannot fully verify the type of the array at runtime due to type erasure. The @SafeVarargs annotation suppresses this warning when the developer is certain no heap pollution occurs.
Is the array created for varargs a new array every time?
Yes. The JVM creates a new array for each varargs call. This means a varargs method called millions of times in a tight loop creates millions of short-lived arrays, which can increase GC pressure. In performance-critical code where the argument count is always fixed, explicit parameters avoid this overhead.
Can you use varargs with constructors?
Yes. Constructors follow the same rules as methods. public Team(String teamName, String... members) is a valid varargs constructor. It is called with new Team("Dev", "Priya", "Rohan", "Sneha") — the JVM packs the member names into a String[].
What is @SafeVarargs and when do you use it?
@SafeVarargs is an annotation on a method with a generic varargs parameter — <T>. It suppresses the "unchecked or unsafe operations" warning the compiler generates for generic varargs. You add it when you are certain the method does not perform unsafe operations on the varargs array — it does not store the array into a variable of a different generic type. Without it, the compiler warns on every call to the method.
Can printf be called with zero format arguments?
Yes — System.out.printf("No placeholders here\n") is valid. The varargs part receives an empty Object[]. The method simply prints the format string with no substitutions. This is the same behaviour as calling any other varargs method with no variable arguments.
Summary
varargs removes the friction of working with a variable number of same-type arguments. The three dots ... tell the compiler to collect whatever is passed into an array — and the calling code reads naturally without array construction boilerplate. The JVM handles all the packaging; the method handles the logic.
The rules are few and memorable: one varargs per method, always last, treated as an array inside, last resort in overload resolution. The gotchas are equally few: check .length before indexing, guard against null, avoid ambiguous autoboxed varargs overloads.
In production code, varargs appears in every logging call, every format string, every builder or collector that accepts a batch of items. Every time a fresher writes System.out.printf("Name: %s, Age: %d", name, age), they are using varargs. Understanding what happens behind that call — array creation, overload resolution, null handling — is exactly what interviewers probe.
What to Read Next
See exactly what gets copied when you call a method.