All topics
library
intermediate

Varargs & Heap Pollution

Understand variable-length arguments, their array backing, and the safety concerns with generic varargs.

Type... lets a method take any number of arguments. The compiler builds an array at the call site.

Varargs = a box that can hold any number of items. But if the box label says 'Strings Only' (generic), the actual box material is 'Any Object' (erasure) — someone could sneak in an Integer.

Key Concepts

1
    int sum(int... nums) {
      int t = 0;
      for (int n : nums) t += n;
      return t;
    }
2
    sum(1, 2, 3);           // compiled as sum(new int[]{1, 2, 3})
    sum();                  // an empty array, not null
    sum(new int[]{1, 2});   // passing an array directly also works
3
The rules.
    one varargs parameter per method
    it must be last
    varargs is the last phase of overload resolution
4
That last point matters.
    void f(int a, int b)   { print("two"); }
    void f(int... a)       { print("varargs"); }
5
    f(1, 2);                // "two" -- the exact match wins
6
So adding a varargs overload never steals calls from an existing method, which is why printf could be added safely.
printf
7
Heap pollution is the generic case. Because of erasure, a T... parameter is really an Object[]:
T...Object[]
8
    @SafeVarargs
    static <T> List<T> listOf(T... items) { return Arrays.asList(items); }
9
Without @SafeVarargs, the compiler warns at every call site, because the array's real type is Object[] and nothing stops unsafe writes into it.
@SafeVarargsObject[]
10
Here is how it actually breaks:
    static <T> T[] toArray(T... args) { return args; }
11
    String[] s = toArray("a", "b");     // ClassCastException at runtime
12
The array created was Object[], and assigning it to String[] fails — at the assignment, with no cast in sight.
Object[]String[]
13
So @SafeVarargs is a promise, not a fix. Only apply it when the method merely reads the array. Never when it stores the array, returns it, or hands it to anything else. It is allowed on static, final and private methods, because those cannot be overridden by something unsafe.
@SafeVarargsstaticfinalprivate
14
Common examples: List.of(E...), String.format(String, Object...), Collections.addAll(Collection, T...).
List.of(E...)String.format(String, Object...)Collections.addAll(Collection, T...)
15
What the interviewer is probing.1. "What is heap pollution and how do generic varargs cause it?" Probing: whether you understand the warning rather than suppressing it. Stalls: "It is just a warning." Moves up: erasure makes a T... parameter an Object[] at runtime, so the array can be given values of the wrong type and a later read throws from a line with no cast.
16
2. "When is @SafeVarargs actually safe to apply?" Probing: whether it is a promise or a fix. Stalls: "When the warning is annoying." Moves up: only when the method just reads the array — never when it stores it, returns it, or passes it on.
17
3. "Given f(int, int) and f(int...), which does f(1, 2) call?" Probing: where varargs sits in overload resolution. Stalls: "The varargs one." Moves up: the fixed-arity method — varargs is the last phase, which is why adding one never steals existing calls.
18
4. "Why can a varargs parameter only be last?" Probing: parsing. Stalls: "Style." *Moves up:* the compiler could not tell where the variable-length list ended and the next parameter began.