Java Tutorial
🔍

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 array

Inside 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

Aspectint... numbers (varargs)int[] numbers (array)
Caller syntaxmethod(1, 2, 3) — no array neededmethod(new int[]{1,2,3}) — array required
Zero argumentsmethod() — validmethod(new int[]{}) or method(null)
Passing existing arraymethod(arr) — works directlymethod(arr) — works directly
Inside the methodTreated as a regular arrayTreated as a regular array
Position in parameter listMust be lastAny position
Multiple varargs in one methodNot allowed — only oneMultiple array params allowed
OverloadingCompiler prefers exact match over varargsNormal overload resolution
JVMCreates array from supplied argumentsArray passed directly — no creation
Readability at call siteClean — sum(1, 2, 3)Verbose — sum(new int[]{1, 2, 3})
When to useWhen the number of arguments genuinely variesWhen 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
  1. ›first is a regular required parameter, and rest is the varargs parameter — Java requires the varargs parameter to come last.
  2. ›findMax(5) passes no extra arguments, so rest becomes an empty int[], and the loop body never runs — max stays 5.
  3. ›findMax(5, 12, 3, 9) packs 12, 3, 9 into rest behind the scenes, and the loop checks each one against the running maximum.
  4. ›Inside the method, rest behaves exactly like a normal int[] — 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 NullPointerException
Exception 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) { } // correct
Compile 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