How many overloads is enough?
After Lesson 5.4, Riya's bill calculator has total for 2, 3 and 4 items. Then a group orders 7 snacks at once. Writing total with 5, 6 and 7 parameters feels wrong.
Sir, do I really need a new overload for every number of items?
No. Write static int total(int... prices). Those three dots mean “zero or more ints”. Call it with 2 prices, 7 prices, or none at all.
But how do I loop over them if I don't know how many there are?
Inside the method, prices is simply an array. The compiler collects whatever you passed into a new int[]. So you use prices.length and a for-each loop, as in Lesson 4.1.
What are varargs?
Varargs (variable-arity parameters), introduced in Java 5, let a method accept zero or more arguments of the same type. A varargs parameter is declared as Type... name; it must be the last parameter, and a method can have only one. Inside the method the parameter is an array of that type (Type[]). At each call site the compiler creates an array containing the trailing arguments; an existing array can also be passed directly.
“Pass as many values as you like.” Java quietly puts them in an array and hands the method that array.
Words you'll meet in this lesson
Type... name that accepts any number of values.String customer in bill(String customer, int... prices).The rules at a glance
| Declaration or call | Valid? | Why |
|---|---|---|
int sum(int... n) | Yes | One varargs parameter, nothing after it |
void bill(String name, int... prices) | Yes | Fixed parameters first, varargs last |
void bad(int... a, String s) | No | varargs parameter must be the last parameter |
void two(int... a, int... b) | No | Only one varargs parameter, and it must be last |
sum() | Yes | The method gets an empty array, n.length == 0 |
sum(new int[]{1, 2, 3}) | Yes | An existing array is passed as it is |
Exam tip: main can be written as public static void main(String... args); it means exactly the same as String[] args. Varargs is purely a compile-time convenience: in the class file the parameter is an ordinary array.
Ways to use varargs
Only varargs
max(int... values). Fine when “none” has a sensible meaning, like a total of 0.
Fixed parameters + varargs
bill(String customer, int... prices). Required values come first, extras after.
“At least one” with a fixed first value
max(int first, int... rest). The compiler now rejects max(), so there's no “empty” case to handle.
Three ways to call the same method
public class Main {
static int total(int... prices) {
int sum = 0;
for (int p : prices) {
sum += p;
}
System.out.println(prices.length + " item(s): Rs " + sum);
return sum;
}
public static void main(String[] args) {
total(); // no arguments → empty array
total(40); // one
total(10, 25, 15, 30); // four
int[] saved = {120, 80};
total(saved); // an array you already have
}
}Varargs in the JDK
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println(String.format("%s paid Rs %d via %s", "Riya", 60, "UPI"));
System.out.printf("%-8s|%5d%n", "Tea", 10);
List<String> menu = List.of("Tea", "Vada", "Samosa");
System.out.println(menu + " " + Arrays.asList(3, 1, 2));
}
}Pitfall with null: total((int[]) null) passes a null array, and the for-each loop throws NullPointerException. A call with no arguments gives an empty array, never null, but a caller can still pass null explicitly. Public APIs often check for it.
What javac does with a varargs call
You declare the parameter with three dots
In the class file this is juststatic String bill(String customer, int... prices)bill(String, int[]). The descriptor is(Ljava/lang/String;[I)Ljava/lang/String;, plus a flag that marks the method as varargs.You call it with loose values
The first argument fills the fixed parameterbill("Kabir", 10, 25, 15);customer. All the remaining ones belong to the varargs parameter.javac rewrites the call to build an array
A newbill("Kabir", new int[]{10, 25, 15});int[3]object is created on the heap at every call. For a call with no extra values, javac createsnew int[0].The method works with the array
Nothing special happens at run time: the JVM sees an ordinary method receiving an ordinary array reference.for (int p : prices) sum += p; // prices.length is 3Overloads: varargs comes last
During overload resolution, varargs methods are considered only in phase 3, after exact matches, widening and boxing. That's why libraries can add fast fixed-arity versions next to a varargs one.static void log(String msg) { … } // chosen for log("x") static void log(String msg, Object... d) { … } // only when extra values are passed
From loose values to an array
Left: the calls you write. Right: the array javac creates for each call and what the method sees.
- The call has four arguments. The first one, a String, matches the fixed parameter
customer; the other three belong toprices. - The method loops over the prices and prints a total of 50.
- With a single price, the loop runs once.
- With no prices,
prices.lengthis 0 and the loop doesn't run: total 0. No special code is needed. - If you already have an
int[], pass it directly. javac doesn't wrap it in another array. - Arguments are matched left to right, so the String must come first, exactly as declared. This doesn't compile.
- javac sees that
billis a varargs method and rewrites the call: the three loose ints becomenew int[]{10, 25, 15}. - At run time, the JVM creates array #1 on the heap. In the bill frame,
pricesholds a reference to it. - Every call builds its own array, here a one-element array #2. (In very hot loops that small allocation is why some APIs add fixed-arity overloads.)
- With no values, javac writes
new int[0]. The parameter is never null for such a call. - An existing
int[]matches the parameter type directly. No copy is made: changes toprices[0]inside the method would changesavedtoo. - Arguments are matched to parameters in order. An int can't fill the String parameter, so no method fits and compilation fails.
Where varargs make APIs pleasant
Most programmers use varargs every day without noticing, through formatting, logging and collection helpers.
Watch it with an example: String.format fills a receipt
String.format(String format, Object... args) is a varargs method from the JDK. Watch the values you pass become an Object[], and each % placeholder take the next one.
- The call: one format string and three values.
formatis declared withObject... args, so any number of values is allowed. - Packing: javac builds
new Object[]{"Riya", 60, "UPI"}. The int 60 is boxed into anInteger, because the array holds Objects. - First placeholder:
%stakesargs[0]and prints it as text: Riya. - Second:
%dtakesargs[1]as a whole number: 60. - Third: the last
%stakesargs[2]: UPI. - Result: “Riya paid Rs 60 via UPI”. Receipt texts, SMS templates and report lines are built this way. Pass too few values and you get a
MissingFormatArgumentExceptionat run time.
1. Formatting text: String.format and printf
String.format(String format, Object... args) takes any number of values to fill the placeholders: receipts, SMS templates, report lines.
2. Logging with placeholders
Logging libraries such as SLF4J offer log.info("Order {} paid by {}", id, user), with a varargs version for any number of values, and fixed one- and two-value versions for speed.
3. Building small collections quickly
List.of("Tea", "Vada"), Set.of(...), Arrays.asList(...) and EnumSet.of(...) accept a few values directly. List.of has fixed versions for up to 10 elements and a varargs version for more.
4. Utility methods in your own code
Helpers like maxOf(int first, int... rest), joinPath(String... parts) or hasAnyRole(String... roles) read naturally at the call site.
Examples are simplified. Varargs create a small array on every call, so performance-critical libraries often add fixed-arity overloads for the common cases.
A canteen bill for any number of items
public class Main {
static String bill(String customer, int... prices) {
int sum = 0;
for (int p : prices) {
sum += p;
}
return customer + ": " + prices.length + " item(s), Rs " + sum;
}
public static void main(String[] args) {
System.out.println(bill("Kabir", 10, 25, 15));
System.out.println(bill("Asha", 60));
System.out.println(bill("Ravi"));
int[] saved = {120, 80};
System.out.println(bill("Meena", saved));
}
}String customer, int... pricesFixed parameter first, the varargs parameter last.for (int p : prices)pricesis anint[], so a for-each loop works.bill("Ravi")No prices:pricesis an empty array, so the total is 0.bill("Meena", saved)An existing array is accepted directly.
A varargs call on the real JVM
Watch the heap: the call average(80, 90, 70) creates a new int[3], and the parameter marks refers to it.
Worked examples: real problems, solved and traced in memory
Three business tasks where the number of values changes from call to call. In each trace, look for the array that javac creates on the heap for the varargs parameter.
Order total with a delivery fee
Problem statement
An order has a fixed delivery fee and any number of item prices. Write orderTotal(int deliveryFee, int... items) and print the total for an order of three items with a Rs 30 fee.
delivery fee 30, items 120, 80, 45
Pay Rs 275
Approach
- The fixed parameter (fee) comes first, the varargs (items) last.
- Start the total at the fee, then add every item.
- The call
orderTotal(30, 120, 80, 45)creates an int[3] for the items.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Highest rating, at least one required
Problem statement
A product page shows the best rating. Write best(int first, int... rest) so that calling it without any rating is a compile error, and print the best of 4, 5, 3 and 5.
ratings 4, 5, 3, 5
Best rating: 5 stars
Approach
- Make the first rating a normal parameter:
best()then won't compile. - Start with
firstas the best so far. - Loop over
restand keep the larger value.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Does the user have any of these roles?
Problem statement
An admin panel lets a user in if they have any of the allowed roles. Write hasAnyRole(String userRole, String... allowed) and check a user with role "OPS" against "ADMIN", "OPS" and "FINANCE".
user role "OPS"; allowed: "ADMIN", "OPS", "FINANCE"
Access granted: true
Approach
- Loop over
allowed. - Return true as soon as one matches with
equals(Lesson 4.4). - If the loop finishes, return false.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 5.6 in one screen
Type... nameaccepts zero or more values; inside the method it is aType[].- Only one varargs parameter per method, and it must be the last.
- javac turns
f(1, 2, 3)intof(new int[]{1, 2, 3});f()getsnew int[0]. - Passing an existing array passes the same object (no copy). An explicit
nullis possible: guard public methods. - In overload resolution varargs come last (phase 3).
- Use
first, ...restto require at least one value. - JDK examples:
String.format,printf,List.of,Arrays.asList.
Interview and viva questions
What are varargs and how do they work internally?
A varargs parameter (Type... name) lets callers pass any number of arguments of that type. The compiler collects them into a new array at the call site and passes the array; inside the method the parameter is an ordinary array. In the class file the parameter type is Type[] and the method carries a varargs flag.
Why must varargs be the last parameter?
So the compiler can tell where the fixed arguments end: every argument after the fixed ones belongs to the varargs array. With a parameter after it, the boundary would be unclear, and allowing two varargs parameters would make it impossible.
What is the difference between f(int... a) and f(int[] a)?
Inside the method there is no difference. At the call site, f(int... a) also accepts loose values like f(1, 2, 3) and no values at all, while f(int[] a) requires an actual array argument.
Why does List.of have overloads for 0 to 10 elements as well as a varargs version?
Performance. Each varargs call allocates an array; fixed-arity overloads avoid that allocation for the common small cases, and the varargs version handles the rest.
What is @SafeVarargs?
An annotation for methods with a generic varargs parameter such as T... items. Generic arrays can cause “heap pollution” warnings; the annotation tells the compiler the method doesn't misuse the array. You'll meet it again with generics (Lesson 11.2).
Practice problems
Write varargs methods, predict overload choices, and avoid the null and empty-array traps. The last three are real business tasks.
Sum of any numbers
Write sum(int... n) and call it with no values, one value and five values.
sum(), sum(7), sum(1, 2, 3, 4, 5)
0 7 15
Show solution and explanation
public class Main {
static int sum(int... n) {
int total = 0;
for (int x : n) total += x;
return total;
}
public static void main(String[] args) {
System.out.println(sum());
System.out.println(sum(7));
System.out.println(sum(1, 2, 3, 4, 5));
}
}How it works: with no values the method receives an empty array, so the loop doesn't run and 0 is returned.
Join words with a separator
Write join(String sep, String... words) that joins the words with the separator, using a StringBuilder.
join(" | ", "Tea", "Vada", "Samosa"), join("-", "2025", "01", "26")Tea | Vada | Samosa 2025-01-26
Show solution and explanation
public class Main {
static String join(String sep, String... words) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < words.length; i++) {
if (i > 0) sb.append(sep);
sb.append(words[i]);
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(join(" | ", "Tea", "Vada", "Samosa"));
System.out.println(join("-", "2025", "01", "26"));
}
}How it works: the separator is a fixed parameter and must come first. The index loop lets us skip the separator before the first word. (The JDK's String.join does the same job.)
Count values above a limit
Write countAbove(int limit, int... values) and count how many day temperatures were above 35.
limit 35; temps 33, 36, 38, 34, 40
Hot days: 3
Show solution and explanation
public class Main {
static int countAbove(int limit, int... values) {
int count = 0;
for (int v : values) {
if (v > limit) count++;
}
return count;
}
public static void main(String[] args) {
System.out.println("Hot days: " + countAbove(35, 33, 36, 38, 34, 40));
}
}How it works: the first argument always fills limit; everything after it goes into the array.
Which overload wins?
Predict the output, then run. Methods: f(long), f(Integer), f(int...), and the calls f(5), f(5, 6), f().
f(5), f(5, 6), f()
long varargs 2 varargs 0
Show solution and explanation
public class Main {
static void f(long x) { System.out.println("long"); }
static void f(Integer x) { System.out.println("Integer"); }
static void f(int... x) { System.out.println("varargs " + x.length); }
public static void main(String[] args) {
f(5);
f(5, 6);
f();
}
}How it works: f(5) is settled in phase 1 by widening to long. The other two calls have no fixed-arity match at all, so only phase 3 (varargs) can handle them.
Average without dividing by zero
Write average(int... marks) that returns 0.0 when no marks are given, instead of producing NaN.
average(), average(80, 90, 70)
0.0 80.0
Show solution and explanation
public class Main {
static double average(int... marks) {
if (marks.length == 0) return 0.0;
int sum = 0;
for (int m : marks) sum += m;
return (double) sum / marks.length;
}
public static void main(String[] args) {
System.out.println(average());
System.out.println(average(80, 90, 70));
}
}How it works: without the check, 0.0 / 0 gives NaN (not a number), which then spreads through any calculation that uses it. Decide what “no values” means for your problem and handle it first.
GST invoice for any number of lines
Write invoice(int gstPercent, int... amounts) that prints the subtotal, GST and grand total for a shop bill.
GST 18%; amounts 250, 400, 150
Items: 3 Subtotal: Rs 800 GST 18%: Rs 144 Total: Rs 944
Show solution and explanation
public class Main {
static void invoice(int gstPercent, int... amounts) {
int subtotal = 0;
for (int a : amounts) subtotal += a;
int gst = subtotal * gstPercent / 100;
System.out.println("Items: " + amounts.length);
System.out.println("Subtotal: Rs " + subtotal);
System.out.println("GST " + gstPercent + "%: Rs " + gst);
System.out.println("Total: Rs " + (subtotal + gst));
}
public static void main(String[] args) {
invoice(18, 250, 400, 150);
}
}How it works: the GST rate is required, so it's a fixed parameter; the line amounts vary per bill. 800 × 18 / 100 = 144.
Send an alert to several phone numbers
Write alert(String message, String... phones) that prints one line per phone number and returns how many messages were sent. Phone numbers must be 10 digits; skip invalid ones.
message "Exam hall changed to B-204"; phones "9876543210", "12345", "9123456789"
SMS to 9876543210: Exam hall changed to B-204 skip invalid: 12345 SMS to 9123456789: Exam hall changed to B-204 2 message(s) sent
Show solution and explanation
public class Main {
static int alert(String message, String... phones) {
int sent = 0;
for (String p : phones) {
if (p.length() != 10) {
System.out.println("skip invalid: " + p);
continue;
}
System.out.println("SMS to " + p + ": " + message);
sent++;
}
return sent;
}
public static void main(String[] args) {
int n = alert("Exam hall changed to B-204", "9876543210", "12345", "9123456789");
System.out.println(n + " message(s) sent");
}
}How it works: the message is fixed; any number of recipients follow. continue (Lesson 3.5) skips bad numbers without stopping the loop.
Cheapest delivery option
Several courier partners quote prices for a parcel. Write cheapest(String... quotes) where each quote looks like "Name:price", and return the cheapest one. Require at least one quote using a fixed first parameter.
"FastShip:85", "QuickPost:70", "CityCourier:92"
Book: QuickPost:70
Show solution and explanation
public class Main {
static String cheapest(String first, String... more) {
String best = first;
for (String q : more) {
if (price(q) < price(best)) best = q;
}
return best;
}
static int price(String quote) {
return Integer.parseInt(quote.substring(quote.indexOf(':') + 1));
}
public static void main(String[] args) {
System.out.println("Book: " + cheapest("FastShip:85", "QuickPost:70", "CityCourier:92"));
}
}How it works: first, ...more makes an empty call impossible. A small helper method extracts the number after the colon, so the comparison stays readable.
Real-world scenario questions (asked in company interviews)
Varargs questions check whether you know what the three dots really compile to. Think of your answer first.
Why does the logger have info(String, Object) and info(String, Object...)?
You notice a logging API offers info(String msg, Object arg), info(String msg, Object a, Object b) and info(String msg, Object... args). Isn't the varargs one enough?
The interviewer is checking: do you know that each varargs call allocates an array?
Show a strong answer
- Functionally yes, but every varargs call creates a new
Object[], even when the log level is turned off and nothing is printed. - Logging is called millions of times; the fixed one- and two-argument versions avoid that garbage for the most common cases.
- Overload resolution prefers fixed arity (phase 1) over varargs (phase 3), so callers get the fast version automatically.
Avoid: “varargs are free”. They are convenient, not free.
max() of nothing
A teammate wrote static int max(int... values). Someone called max() and got ArrayIndexOutOfBoundsException from values[0]. How would you design it so the mistake can't happen?
The interviewer is checking: can you use the type system instead of run-time checks?
Show a strong answer
- Change the signature to
max(int first, int... rest). Nowmax()doesn't compile. - Start with
firstand loop overrest; the empty-array case needs no special code. - If the signature must stay, check
values.length == 0and throwIllegalArgumentExceptionwith a clear message.
Avoid: returning 0 for an empty list; it hides the bug.
The discount that changed the saved cart
A method applyCoupon(int... prices) lowers prices[0] and returns the total. It was called with a cart's saved int[], and afterwards the cart itself showed the discounted price even though the order was cancelled. Explain.
The interviewer is checking: do you know that passing an array to varargs passes the same object?
Show a strong answer
- An existing array is passed directly, not copied, so the method changed the cart's own array.
- Fix: don't modify the parameter; work on a copy (
prices.clone()) or compute the total without writing to the array. - Called with loose values, the bug would never show, because javac creates a fresh array each time. That's why it slipped through testing.
Avoid: assuming varargs always give the method its own private array.
Varargs or a List parameter?
A method notifyUsers(String... userIds) is called from one place, with a list of 50,000 IDs loaded from the database: notifyUsers(ids.toArray(new String[0])). Is varargs the right choice here?
The interviewer is checking: do you know when varargs help and when they don't?
Show a strong answer
- Varargs shine when callers type a few values by hand. Here the data is already a collection, so the conversion to an array is extra work and noise.
- A
List<String>(orCollection<String>) parameter states the intent better and avoids copying 50,000 references. - If both styles are needed, offer both, with the varargs version calling the List version via
List.of(ids).
Avoid: using varargs everywhere just because it looks flexible.