skip to content

What problem does @SafeVarargs solve, and where can it be applied?

level: seniorimportance: should knowfreq 38%

answer

  1. Generic varargs -> array of non-reifiable type -> heap-pollution warning
  2. Annotation = author's promise the method is safe (reads only, no leak)
  3. Suppresses warning at declaration AND all call sites
  4. Only on un-overridable: static, final, private (Java 9+), constructors
  5. Non-final instance method => compile error
  6. Doesn't verify safety — it's an assertion; SOURCE retention

basics

~20 s

When a method takes a varargs of a generic type, the compiler warns it might not be type-safe because of how generics work. @SafeVarargs is you promising the method doesn't misuse that array, which removes the warning at the method and at every call site.

solid answer

~50 s

@SafeVarargs suppresses the 'unchecked' / 'possible heap pollution' warning that arises when a method has a varargs parameter of a non-reifiable (generic) type, like T... or List<String>.... The warning exists because varargs are implemented as arrays, but arrays of generic types can't fully enforce their element type at runtime due to type erasure, risking heap pollution. By annotating the method, the author asserts the implementation is safe — it only reads from the varargs array and never stores an incompatible element or leaks the array where it could be corrupted. It removes the warning both inside the method and at all call sites. It can only go on methods that cannot be overridden: static methods, final instance methods, private instance methods (since Java 9), and constructors. Putting it on a non-final, overridable instance method is a compile error, because the author can't vouch for subclass overrides.

code

java · 12 lines
java
import java.util.ArrayList;
import java.util.List;

class SafeVarargsDemo {
    @SafeVarargs                 // static => allowed; reads only => actually safe
    static <T> List<T> listOf(T... items) {
        List<T> out = new ArrayList<>();
        for (T item : items) out.add(item); // only reads the array
        return out;
    }
    // @SafeVarargs <T> List<T> bad(T... xs) {...} // ERROR: instance method not final/static/private
}

go deeper

for a junior

May only recognize that it relates to varargs and generics; not expected to use it.

for a middle

Knows it suppresses a generic-varargs warning and that it goes on static/final methods; can apply it to a simple helper.

for a senior

Explains heap pollution from erasure+arrays, why it covers call sites, the exact allowed targets (incl. private since Java 9), and the author's safety responsibility.

for a principal

Reasons about API surface design with generic varargs, the binary/source compatibility of adding the annotation, and when to avoid generic varargs entirely in favor of explicit collections.

## Two background concepts **Varargs** (variable arguments): a method like `void log(String... parts)` accepts any number of `String` arguments. Under the hood the compiler packs them into an **array** — `parts` is really a `String[]`. **Type erasure & non-reifiable types:** Java generics are a compile-time feature. At runtime, the type parameter `T` (and parameterized types like `List<String>`) is **erased** — the JVM doesn't fully know the element type. Types whose full type information isn't available at runtime are called **non-reifiable**. Arrays, by contrast, *do* carry their element type at runtime, so combining the two — an **array of a generic type** — is inherently leaky. ## The warning and 'heap pollution' When you write a generic varargs method: ```java static <T> List<T> listOf(T... items) { ... } ``` the compiler creates a `T[]` array, but it can't guarantee at runtime that the array really contains only `T`s. This mismatch is called **heap pollution**: a variable of a parameterized type refers to an object that is not of that parameterized type, which can later blow up with an unexpected `ClassCastException`. So the compiler issues an **'unchecked' / 'Possible heap pollution from parameterized vararg type'** warning at both the **declaration** and every **call site**. ### How it can actually go wrong The danger is real if the method **stores into** the array or **leaks the array reference** to code that treats it as a plain `Object[]`: ```java static <T> T[] dangerous(T... arr) { Object[] o = arr; o[0] = Integer.valueOf(42); // pollutes a String[] if called with Strings return arr; // later reads -> ClassCastException } ``` ## What @SafeVarargs asserts `@SafeVarargs` is the author's **promise** that the method is in fact safe — typically because it only **reads** from the varargs array and never **writes** an incompatible element nor **exposes** the array to be mutated. With that promise, the compiler suppresses the unchecked/heap-pollution warning **both in the method body and at all call sites** — which is the key benefit over `@SuppressWarnings("unchecked")`, which only silences the declaration site, not callers. It is **`SOURCE`**-retained — purely a compile-time directive. ## Where it is allowed The author can only vouch for an implementation they fully control, so `@SafeVarargs` is restricted to methods that **cannot be overridden**: - **`static` methods** - **`final` instance methods** - **`private` instance methods** (allowed since **Java 9**) - **constructors** Putting it on a **non-final, non-static, non-private instance method** is a **compile error** (an overriding subclass could be unsafe). Applying it to a method that has **no varargs parameter** is also an error/warning — there's nothing to assert. ## Relationship to other annotations - It is a more **targeted, correct** tool than `@SuppressWarnings({"unchecked","varargs"})` for this specific case, and it also covers call sites. - JDK examples: `Arrays.asList(T...)`, `List.of(E...)`, `EnumSet.of(...)`, `Collections.addAll(...)` are annotated `@SafeVarargs`. ## Responsibility caveat The annotation **does not verify** safety — it **suppresses** the warning based on your assertion. If you annotate an actually-unsafe method, you've hidden a real risk. So only apply it after confirming the method merely reads the array and never lets it escape mutably. ## Takeaway Use `@SafeVarargs` on un-overridable generic-varargs methods that only read their varargs array, to remove the heap-pollution warning at the declaration and all call sites — but only when you've verified the method truly is safe.

  • Why can't @SafeVarargs be applied to a non-final, non-static instance method?
    Because such a method can be overridden, and the author cannot vouch for the safety of an unknown subclass override; the language therefore makes it a compile error.
  • How does @SafeVarargs differ from @SuppressWarnings("unchecked") for this case?
    @SuppressWarnings only silences the warning at the declaration, while @SafeVarargs also suppresses the heap-pollution/unchecked warning at every call site, which is the real source of the noise.

saying these in an interview costs you the question

  • Putting it on a non-final, overridable instance method (compile error)
  • Thinking it makes an unsafe method safe rather than just suppressing the warning
  • Confusing it with @SuppressWarnings (which doesn't cover call sites)
  • Not knowing why generic varargs are risky (erasure + arrays = heap pollution)
  • Annotating a method that has no varargs parameter

context