Using Ellipsis to Accept a Variable Number of Arguments in Java

Java's varargs (short for variable arguments) let a method accept an arbitrary number of parameters of the same type. The feature was introduced in Java 5 via the ... ellipsis syntax and is now ubiquitous β€” String.format, Arrays.asList, List.of and Collections.addAll all rely on it.

Basic syntax

Place three dots between the type and the parameter name:

public static int sum(int... numbers) {
    int total = 0;
    for (int n : numbers) total += n;
    return total;
}

// All of these are valid calls:
sum();                    // 0
sum(1);                   // 1
sum(1, 2, 3);             // 6
sum(new int[]{1, 2, 3});  // 6 β€” you can pass an array directly

Inside the method, numbers behaves as a normal int[] β€” the compiler wraps your arguments into an array automatically.

Rules to remember

  • A varargs parameter must be the last parameter of the method signature.
  • A method can have at most one varargs parameter.
  • The varargs type can be any type, including objects, generics, and arrays.
// βœ… Legal
public void log(String level, Object... args) { }

// ❌ Illegal: varargs must come last
public void log(Object... args, String level) { }

// ❌ Illegal: only one varargs allowed per method
public void log(String... tags, Object... args) { }

A realistic example

A tiny logging helper that accepts a format string and any number of parameters:

public class Logger {
    public static void info(String format, Object... params) {
        String message = String.format(format, params);
        System.out.println("[INFO] " + message);
    }

    public static void main(String[] args) {
        info("User %s logged in from %s", "alice", "192.168.1.10");
        info("Started in %d ms", 342);
    }
}

Common pitfalls

Passing null to a varargs parameter

Calling doStuff(null) on a method declared as doStuff(Object... args) passes a null array, not an array containing null. Guard against it:

public void doStuff(Object... args) {
    if (args == null || args.length == 0) return;
    // ...
}

Overload ambiguity

Varargs methods interact oddly with overloading. The compiler prefers the most specific non-varargs match:

public static void call(int a, int b) { System.out.println("two ints"); }
public static void call(int... a)    { System.out.println("varargs");   }

call(1, 2); // prints "two ints" β€” non-varargs wins

Generic varargs and heap pollution

Mixing varargs with generics triggers an unchecked warning because Java creates an Object[] under the hood. Use @SafeVarargs on the method if it only reads from the array:

@SafeVarargs
public static <T> List<T> listOf(T... items) {
    return Arrays.asList(items);
}

When not to use varargs

If a method is called millions of times in a tight loop, the implicit array allocation can become a bottleneck. For performance-critical paths, provide fixed-arity overloads (sum(int, int), sum(int, int, int)) alongside the vararg version β€” this is how the JDK's List.of is implemented.

For everyday application code, varargs are idiomatic and readable. Prefer them over forcing callers to build an array manually.