Back to Methods & Varargs
Q1Question 1 of 3

What are varargs and what are the rules?

Key Concepts
  • Varargs (String... args) lets a method accept zero or more arguments of the same type
  • The compiler converts varargs to an array internally – the method receives a regular array
  • Varargs must be the LAST parameter: process(String id, BigDecimal... amounts) is OK
  • Only ONE varargs parameter per method – two varargs parameters do not compile
  • Varargs creates a new array on every call – avoid in hot paths for performance
  • Varargs can cause ambiguity with overloaded methods – the compiler picks the most specific match

Definition

Varargs (type... name) lets a method accept zero or more arguments of the same type. The compiler creates an array behind the scenes.

Varargs is syntactic sugar for arrays:

  • Syntax: public void process(BigDecimal... amounts) accepts zero or more BigDecimal arguments.
  • Internally: The compiler converts it to process(BigDecimal[] amounts). The bytecode is identical.
  • Calling: You can pass individual values: process(a, b, c) or an array: process(new BigDecimal[]{a, b, c}) or nothing: process().
  • Rules: Must be last parameter. Only one per method.

A bank deposit slip with a variable number of amount fields. You can write one amount, five amounts, or no amounts at all. The teller processes whatever you provide, treating them all as a list internally.

Understanding the Concepts

How do varargs work?

JAVA
public class TransferService {
    // Varargs: accepts zero or more amounts
    public BigDecimal totalDeposit(BigDecimal... amounts) {
        BigDecimal total = BigDecimal.ZERO;
        for (BigDecimal amount : amounts) {
            total = total.add(amount);
        }
        return total;
    }
}

TransferService svc = new TransferService();

// Call with individual values
BigDecimal t1 = svc.totalDeposit(
    new BigDecimal("50000"),
    new BigDecimal("12000"),
    new BigDecimal("8000")
);  // 70000

// Call with zero arguments
BigDecimal t2 = svc.totalDeposit();  // 0

// Call with an array
BigDecimal[] arr = {new BigDecimal("1000"), new BigDecimal("2000")};
BigDecimal t3 = svc.totalDeposit(arr);  // 3000

What are the rules?

JAVA
// Rule 1: varargs must be the LAST parameter
public void process(String accountId, BigDecimal... amounts) { }  // OK
// public void process(BigDecimal... amounts, String accountId) { }  // ERROR

// Rule 2: only ONE varargs parameter
// public void process(int... ids, String... names) { }  // ERROR

// Rule 3: varargs can receive zero arguments
public void log(String... messages) {
    // messages.length can be 0
}
log();  // OK: messages is an empty array

How does varargs interact with overloading?

Varargs has lower priority than exact matches:

JAVA
static void print(String s)       { System.out.println("exact: " + s); }
static void print(String... s)    { System.out.println("varargs: " + Arrays.toString(s)); }

print("hello");           // "exact: hello" (exact match wins)
print("a", "b");          // "varargs: [a, b]" (only varargs matches)
print();                  // "varargs: []" (only varargs matches zero args)

What is the ambiguity trap?

JAVA
static void process(int... nums) { System.out.println("int varargs"); }
static void process(long... nums) { System.out.println("long varargs"); }

// process(1, 2, 3);  // COMPILE ERROR: ambiguous – both match via widening!

When two varargs overloads compete and neither is more specific, the compiler rejects the call.

What about @SafeVarargs?

With generic varargs, the compiler warns about heap pollution:

JAVA
// Warning: Possible heap pollution from parameterized vararg type
public static <T> List<T> listOf(T... items) {
    return Arrays.asList(items);
}

// Suppress with @SafeVarargs (only on final or static methods)
@SafeVarargs
public static <T> List<T> safeListOf(T... items) {
    return Arrays.asList(items);  // safe: no modification of the array
}

What about performance?

Varargs creates a new array on every call:

JAVA
// Each call allocates a new BigDecimal[] array
for (int i = 0; i < 1_000_000; i++) {
    svc.totalDeposit(amount1, amount2);  // new BigDecimal[2] each time
}

// For hot paths, pass a pre-built array instead
BigDecimal[] amounts = {amount1, amount2};
for (int i = 0; i < 1_000_000; i++) {
    svc.totalDeposit(amounts);  // reuses the same array
}

What are common mistakes?

  • Varargs not last. (int... ids, String name) does not compile.
  • Two varargs. (int... a, String... b) does not compile.
  • Passing null explicitly. process(null) passes a null array, not a one-element array containing null. Use process((String) null) or process(new String[]{null}).
  • Overloading ambiguity. Two varargs overloads with compatible types cause compile errors.

What should a senior engineer know?

  • List.of() and Map.of() use varargs internally. List.of(1, 2, 3) calls List.of(E... elements).
  • printf/String.format use varargs: String.format("%s: %d", name, count).
  • In Spring, @Value annotation can inject arrays: @Value("${rates}") double[] rates.
  • Varargs is a compiler feature. At bytecode level, it is just an array parameter. Reflection sees BigDecimal[], not BigDecimal....

Visual Overview

DIAGRAM
  Source:  process(BigDecimal... amounts)
  Bytecode: process(BigDecimal[] amounts)
  → identical at runtime

  Calling:
    process(a, b, c)           → compiler creates new BigDecimal[]{a, b, c}
    process()                  → compiler creates new BigDecimal[]{}
    process(existingArray)     → uses the array directly

  Rules:
    must be LAST parameter
    only ONE per method
    zero arguments allowed
    exact match wins over varargs in overloading

Key Takeaway

Varargs lets a method accept zero or more arguments of the same type using the type... name syntax. The compiler converts it to an array internally, so the method receives a regular array. You can call it with individual values, with an array, or with no arguments at all.

The rules are simple: varargs must be the last parameter, and only one varargs parameter is allowed per method. In overloading, an exact match beats a varargs match. Two competing varargs overloads can cause ambiguity.

In banking, varargs is useful for methods that accept a variable number of amounts, IDs, or flags. But for hot paths, pass a pre-built array to avoid the allocation overhead of creating a new array on every call.

One sentence to remember: varargs (type... name) is syntactic sugar for an array parameter – must be last, only one per method, and creates a new array on each call.

---

Share
Use ← → arrow keys to navigate