When should you use varargs, and what are common pitfalls and alternatives?
answer
- Use for natural 'zero-or-more of one type' + inline literals
- Require ≥1? Add a fixed first param: min(int first, int... rest)
- Per-call array allocation on hot paths
- Generics → prefer List<T>, beware heap pollution
- null to Object... is ambiguous — cast it
basics
~20 sUse varargs when a method naturally takes 'any number' of one type, like log(String...) or sum(int...). Avoid forcing it where a list is clearer, and require at least one mandatory value with a fixed first parameter when zero arguments make no sense.
solid answer
~50 sVarargs is the right tool when a method legitimately takes an arbitrary count of same-typed values and writing them inline reads better than wrapping them in a list — formatting, building collections, logging, min/max helpers. The main pitfalls: it silently allows zero arguments, so if at least one value is mandatory, enforce it by declaring a fixed first parameter plus varargs, e.g. min(int first, int... rest); it allocates an array per call (matters on hot paths); generic varargs risks heap pollution; and it can create overload ambiguity. Prefer a List or Collection parameter when the count is usually large, when you'd otherwise pass an array you already have, or when generics are involved. For performance-sensitive APIs, follow the JDK pattern of small fixed-arity overloads plus a varargs catch-all. Overall, reach for varargs for genuine convenience, not as a default for every multi-value method.
code
java · 12 lines// Require at least one value: fixed first param + varargs rest
static int min(int first, int... rest) {
int m = first;
for (int v : rest) m = Math.min(m, v);
return m;
}
min(3, 1, 4); // ok -> 1
min(7); // ok -> 7
// min(); // compile error: 'first' is required
// Prefer a collection when generics or large counts are involved:
static <T> void process(List<T> items) { /* no array, no heap pollution */ }go deeper
Recognizes varargs as the way to accept many values and can name an example like String.format; basic 'use it when there can be many' intuition.
Chooses varargs for genuine convenience, enforces a required minimum with a fixed leading parameter, and knows the array-allocation and zero-argument pitfalls.
Weighs varargs vs List<T> on generics, hot-path allocation, and overload ambiguity; applies the small-fixed-overload-plus-varargs JDK idiom deliberately.
Sets team/library conventions for when varargs is acceptable, balancing call-site ergonomics against allocation, type-safety, and API-evolution risks across an entire codebase.
## Decide by intent Varargs exists to make a *natural* 'zero-or-more of the same thing' API read cleanly at the call site. Good fits: - **Formatting / messaging:** `String.format(fmt, Object...)`, logging `info(msg, Object...)`. - **Collection factories:** `List.of(E...)`, `Set.of(E...)`, `EnumSet.of(...)`. - **Aggregation helpers:** `Math`-style `max(int...)`, `concat(String...)`. The test: would callers usually write loose literals (`of("a","b","c")`)? If yes, varargs improves readability. If they almost always already hold a `List` or array, a collection parameter is cleaner. ## Pitfall 1 — zero arguments are silently allowed A plain `T...` accepts an empty call. If your method requires **at least one** value, `min()` with no args is nonsensical (what's the minimum of nothing?) yet compiles. The idiom is to **make the first element a fixed parameter**: ```java static int min(int first, int... rest) { ... } // min() now won't compile ``` This enforces the minimum count at compile time and is exactly how the JDK shapes such APIs. ## Pitfall 2 — per-call array allocation Every loose-argument call allocates an array. Irrelevant for ordinary code, but on hot paths it creates GC pressure. Mitigate with small fixed-arity overloads (the `List.of` pattern) or by passing a reused array. ## Pitfall 3 — generics and heap pollution `List<T>... ` forces an unsound generic array and an unchecked warning; only read-only methods are safe (`@SafeVarargs`). When generics are involved, a `List<T>` parameter is usually the safer choice. ## Pitfall 4 — overload ambiguity and surprise Mixing varargs with other overloads can produce ambiguous calls or silently redirect calls (fixed-arity always beats varargs). Keep overload sets simple, and beware that adding a fixed overload changes existing behavior. ## Pitfall 5 — `null` and array confusion Passing a single `null` to an `Object...` method is ambiguous between 'null array' and 'array of one null'; the compiler warns and treats it as the array. Cast explicitly when you mean one null element: `m((Object) null)`. ## Alternatives - **`List<T>` / `Collection<T>` parameter:** clearer for large or already-collected inputs, no array allocation surprise, safe with generics. Costs a `List.of(...)` wrap at the call site. - **Builder / fluent API:** when arguments are heterogeneous or have meaning beyond 'one more value.' - **Explicit fixed overloads:** when there are only a couple of common counts. ## Rule of thumb Use varargs for genuine *zero-or-more-of-the-same* convenience where inline literals read well; guard a required minimum with a fixed leading parameter; avoid it for generic-heavy, hot-path, or already-collection-shaped inputs. It's a readability tool, not a default.
- How do you force a varargs method to require at least one argument?Split the signature so the first value is a fixed parameter and the rest are varargs: method(T first, T... rest). Now a zero-argument call won't compile, while one-or-more still works naturally.
- When would you prefer a List<T> parameter over T... varargs?When callers usually already hold a collection, when the count is typically large (avoiding per-call array allocation), or when generics are involved (sidestepping heap pollution and the unchecked warning). The trade-off is callers must wrap loose values in List.of(...).
saying these in an interview costs you the question
- Using varargs for a required-non-empty input without a fixed leading parameter
- Defaulting every multi-arg method to varargs regardless of fit
- Using generic varargs where a List<T> parameter would be safer
- Ignoring that callers can pass zero arguments