Java Varargs

In the last lesson you learned about Java method overloading. But what if you do not know how many values the caller will pass? Java solves this with varargs, short for variable arguments, which let a method take any number of values.

πŸ€” Why do we need varargs?

Say you want a sum method that adds however many numbers someone gives it. Without varargs you are stuck:

  • Overload once per count? There is no last version. Someone always wants one more number.
// ❌ This never ends. You cannot cover every possible count.
static int sum(int a, int b) { ... }
static int sum(int a, int b, int c) { ... }
static int sum(int a, int b, int c, int d) { ... }
// ... forever
  • Force the caller to build an array? That pushes clunky work onto every caller.

You cannot know in advance how many arguments the caller has, so a fixed parameter list does not fit. Varargs fix this: one method takes two numbers or fifty, and the caller just passes as many as they want.

Picture a shop checkout. The cashier does not need to know your item count before you arrive. Two items or twenty, same cashier. Varargs are that cashier.

🧩 The varargs syntax

Write three dots ... right after the type. That is the whole syntax.

// The ... means "any number of values of this type".
returnType methodName(Type... name) {
// name behaves like an array here
}

Read int... numbers as β€œany number of ints”. When the caller passes several loose values, Java collects them into an array and hands it to your method:

  • numbers inside the method is just an int[].
  • Loop over it with a for-each or an index.
  • Read the count with .length.
  • Index into it, like numbers[0], for a specific position.

πŸ’‘ A sum method with varargs

This sum takes any number of ints and adds them all.

public class Adder {
// int... numbers means "any number of ints", arriving as an array.
static int sum(int... numbers) {
int total = 0;
for (int n : numbers) { // βœ… numbers is just an int[] inside
total = total + n;
}
return total;
}
public static void main(String[] args) {
System.out.println(sum(5, 3)); // two values
System.out.println(sum(1, 2, 3, 4, 5)); // five values
System.out.println(sum()); // even zero values
}
}
  • sum(int... numbers) declares one varargs parameter that serves every call.
  • numbers is an array, so the for-each adds each value to total.
  • The calls pass two, five, and zero ints.
  • Zero arguments give an empty array, so the loop runs zero times and total stays 0.

Output

8
15
0

The third line is 0. Calling sum() with nothing did not crash and did not give null. It gave an empty array, which summed to zero.

πŸ” It really is an array inside

The varargs parameter is a real array inside the method, with a real .length you can loop and index. Everything you know about arrays just works. Here a method lists any number of words.

public class Printer {
static void show(String... words) {
// words is a String[] here, so .length and looping both work.
System.out.println("You passed " + words.length + " words:");
for (String w : words) {
System.out.println("- " + w);
}
}
public static void main(String[] args) {
show("apple", "banana", "cherry");
}
}
  • show(String... words) accepts any number of strings, arriving as a String[].
  • words.length gives the count, so we print 3.
  • The for-each prints each word with a dash in front.

The ... is a convenience for the caller, not the method. It lets them pass loose values instead of building an array by hand.

Output

You passed 3 words:
- apple
- banana
- cherry

Because it is a real array, you can pass a ready-made String[] straight in. No unpacking needed.

public class PassArray {
static void show(String... words) {
for (String w : words) {
System.out.println("- " + w);
}
}
public static void main(String[] args) {
String[] fruits = { "apple", "banana", "cherry" };
show(fruits); // βœ… passing a ready-made array also works
}
}

So a varargs method accepts both styles: loose values like show("apple", "banana"), or a finished array like show(fruits). Both reach the method as the same String[].

πŸ–¨οΈ The printf-style example

You have already used varargs: System.out.printf is one. The format text comes first, then any number of values to drop into it. This method follows the same shape: a label, then any number of scores.

public class Report {
// label is fixed and first; scores is varargs and last.
static void printScores(String label, int... scores) {
System.out.println(label + " (" + scores.length + " scores):");
for (int s : scores) {
System.out.println(" " + s);
}
}
public static void main(String[] args) {
printScores("Riya", 90, 85, 100);
printScores("Alex"); // no scores yet, that is fine
}
}
  • label is a fixed parameter, so the first argument fills it.
  • scores is varargs, so every argument after the label lands in the scores array.
  • The second call gives only a label, so scores is empty and the loop prints nothing.

Same shape as printf: one fixed thing in front, then a flexible tail of values.

Output

Riya (3 scores):
90
85
100
Alex (0 scores):

Here is the real printf. The %s and %d are placeholders, and the loose values after the text fill them in order.

String name = "Alex";
int score = 90;
// printf takes the text first, then any number of values to insert.
System.out.printf("%s scored %d%n", name, score);

Output

Alex scored 90

Varargs are everywhere: printf, String.format, and List.of all use them.

πŸ“ The rules of varargs

Break these and the code will not compile:

  • A method can have only one varargs parameter.
  • The varargs parameter must be the last one. Fixed parameters come first.
  • Both rules exist so Java can tell where the loose values stop and start. Two varargs, or one in the middle, would be ambiguous.

Here is a valid mix of a fixed parameter and varargs.

public class Greeter {
// βœ… fixed parameter first, varargs last
static void greet(String greeting, String... names) {
for (String name : names) {
System.out.println(greeting + ", " + name + "!");
}
}
public static void main(String[] args) {
greet("Hello", "Riya", "Arjun", "Alex");
}
}
  • greeting is the fixed parameter, so it grabs the first argument, "Hello".
  • names is varargs and last, so everything after is bundled into it.
  • The loop greets each name with the shared greeting.

Output

Hello, Riya!
Hello, Arjun!
Hello, Alex!

πŸ”€ Varargs and overloading

Varargs are greedy: sum(int... nums) matches zero, one, two, or more ints, so it overlaps almost any other sum. When two methods both fit a call, Java prefers a fixed-parameter method over a varargs method. Varargs are the fallback, used only when no exact match exists.

public class Overloaded {
static void show(int a, int b) {
System.out.println("fixed two-int version");
}
static void show(int... nums) {
System.out.println("varargs version, count = " + nums.length);
}
public static void main(String[] args) {
show(1, 2); // two ints: exact fixed match wins
show(1, 2, 3); // three ints: only varargs fits
show(); // zero ints: only varargs fits
}
}
  • show(1, 2) matches the fixed two-int method exactly, so Java picks that.
  • show(1, 2, 3) has no fixed match, so varargs takes it.
  • show() has no fixed match, so varargs takes it with an empty array.

Output

fixed two-int version
varargs version, count = 2
varargs version, count = 0

Mixing them is fine as long as the fixed version wins ties. The danger is when two overloads match equally well and Java cannot choose, which is the next trap.

⚠️ Common Mistakes

Putting varargs before another parameter. The ... must be last. Anything after it confuses Java about where the loose values end.

// ❌ Does not compile: varargs is not last
static void greet(String... names, String greeting) { }
// βœ… Fixed parameter first, varargs last
static void greet(String greeting, String... names) { }

Using more than one varargs. A method gets only one ... parameter. Java could not split the loose values between two.

// ❌ Does not compile: two varargs parameters
static void mix(int... a, int... b) { }
// βœ… Only one varargs; combine the rest into it
static void mix(int... all) { }

Writing ambiguous overloads. If two overloads match a call equally well, Java refuses to guess and the code will not compile.

// ❌ Ambiguous: which one should join("x") pick? Both fit equally.
static void join(String first, String... rest) { }
static void join(String... all) { }
// βœ… Keep one clear signature instead
static void join(String... all) { }

The fix is to drop one clashing overload, or give them clearly different fixed parameters.

Forgetting it can be empty. Zero values give an empty array, never null. No null check needed; the loop just runs zero times.

// βœ… Safe with zero arguments: nums is empty, not null
static int sum(int... nums) {
int total = 0;
for (int n : nums) total += n; // runs zero times when empty
return total;
}

βœ… Best Practices

  • Use varargs when the count truly varies. Summing numbers, joining words, logging several values.
  • Keep the varargs parameter last. Put every required parameter before it.
  • Treat it as an array inside. Reach for .length and a loop.
  • Avoid overloading a varargs method. If you must, keep one overload a clearer match to dodge the ambiguity error.
  • Do not reach for varargs by reflex. If you always pass exactly two values, plain parameters are clearer and safer.

🧩 What You’ve Learned

  • βœ… Varargs let a method accept any number of arguments, written with Type....
  • βœ… Inside the method, the varargs parameter behaves exactly like an array, with .length, indexing, and loops.
  • βœ… The caller can pass zero, one, or many loose values, or even an existing array; zero gives an empty array, not null.
  • βœ… A method can have only one varargs parameter, and it must be the last one.
  • βœ… You can mix fixed parameters first, then the varargs at the end, just like printf.
  • βœ… When overloading, Java prefers a fixed-parameter match over varargs, and refuses to compile ambiguous overloads.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does varargs let a method do?

    Why: Varargs let one method take a variable number of arguments, from zero to many.

  2. 2

    How is the varargs parameter treated inside the method?

    Why: Java bundles the passed values into an array, so you loop and index it like any array.

  3. 3

    Where must the varargs parameter appear in the parameter list?

    Why: The varargs parameter must be the last one, with fixed parameters before it.

  4. 4

    When both a fixed-parameter method and a varargs method match a call, which does Java pick?

    Why: Java prefers the exact fixed-parameter match and uses varargs only as a fallback.

πŸš€ What’s Next?

With methods behind you, the next step is bundling data and behavior into your own types. That is what classes are about, and they are the doorway to object-oriented programming in Java.

Java Introduction to Classes

Share & Connect