skip to content

When is @SafeVarargs legitimately applicable, and what does it actually do?

level: seniorimportance: should knowfreq 45%

answer

  1. Author's assertion, suppresses warning — no runtime effect
  2. Safe = only reads varargs, array never escapes, never written
  3. Suppresses at declaration AND call sites
  4. Only on non-overridable: static / final / private / constructor
  5. Misuse hides a real ClassCastException

basics

~20 s

@SafeVarargs tells the compiler 'I checked this generic varargs method, it does not misuse the array, so stop warning.' Use it only when the method just reads the varargs and never lets the array escape or get the wrong type stored in it.

solid answer

~50 s

@SafeVarargs is an assertion by the method author that the method does not perform unsafe operations on its generic varargs parameter, so the compiler suppresses the heap-pollution/unchecked warning at both the declaration and every call site. It does not make anything safe; it silences a warning you take responsibility for. It is legitimately applicable only when the method does not store anything into the varargs array and does not expose (return or otherwise leak) the array to code that could pollute it — typically methods that only iterate over the elements to read them. Because of those rules, it may only be placed on methods that cannot be overridden: static methods, final instance methods, private instance methods, and constructors. Annotating a method that leaks or writes the array defeats the safety net and can reintroduce a hidden ClassCastException.

code

java · 12 lines
java
// SAFE: only reads the varargs, array never escapes -> @SafeVarargs is appropriate.
@SafeVarargs
static <T> List<T> listOf(T... items) {
    List<T> result = new ArrayList<>();
    for (T item : items) result.add(item); // read-only over elements
    return result;                          // returns a copy, not the array
}

// UNSAFE: do NOT annotate — leaks the array, enabling heap pollution.
static <T> T[] leak(T... items) {
    return items; // escaping array reference can be polluted later
}

go deeper

for a junior

Knows @SafeVarargs silences the generic-varargs warning and should only be used when the method is genuinely safe.

for a middle

States that it is an author assertion with no runtime effect and that the method must only read the varargs and not leak the array.

for a senior

Enumerates the safety rules (no store, no escape) and the placement restriction (static/final/private/constructor), and contrasts it with narrow @SuppressWarnings.

for a principal

Sets API guidelines for generic-varargs methods, explains why override-ability breaks the assertion, and advises designs (immediate copy to List) that make safety provable rather than asserted.

## The problem it addresses As covered in the related question, a **generic varargs** method (`<T> void f(T... args)`) forces the compiler to build an array of a **non-reifiable** type (because generics are **erased** at runtime). Such an array's static type overstates what the runtime can enforce, enabling **heap pollution** — a typed reference pointing to wrongly-typed contents, which surfaces later as a `ClassCastException`. So the compiler emits an unchecked/heap-pollution warning. Many such methods are *actually* safe — e.g. `List.of(T...)` or a method that just loops over the arguments printing them. Forcing every author and every caller to suppress the warning manually would be noise. `@SafeVarargs` is the targeted tool for the author to say "this one is fine." ## What @SafeVarargs does — and does not do It is a pure **assertion**. Applying it: - **Suppresses** the heap-pollution warning at the **method declaration**, and - **Suppresses** the unchecked warning at **every call site** that passes generic arguments. It changes **no runtime behavior** and adds **no checks**. If your method is actually unsafe, `@SafeVarargs` simply hides the warning and lets the latent bug ship. The safety is entirely your claim. ## When it is legitimately applicable (the rules of safety) The method must not do anything that could pollute or expose the varargs array. Concretely: 1. **It must not store anything into the array.** Writing `args[0] = something` of a different runtime type pollutes it. 2. **It must not let the array escape** — do not `return args`, assign it to a field, or pass the *array itself* to code that might store into it or expose it. (Passing individual *elements* is fine; passing the whole array is the risk, because the receiver could write to it or leak it.) The canonical safe shape is a method that **only reads** the varargs — iterates over the elements and consumes them. Examples in the JDK: `Arrays.asList`, `Collections.addAll`, `List.of`, `EnumSet.of`. ## Where you are allowed to put it Because the safety of the implementation must be guaranteed by the *author* and cannot be re-checked for overrides, `@SafeVarargs` may only be applied to methods that **cannot be overridden**: - `static` methods, - `final` instance methods, - `private` instance methods (since Java 9), - constructors. Putting it on a non-final, overridable instance method is a **compile error**, because a subclass could override it with an unsafe body that the annotation would wrongly bless. ## The alternative and the judgment call If you cannot satisfy the rules, do **not** use `@SafeVarargs`. Options: - Redesign so the array never leaks (often: copy into a `List` immediately and work with that). - Or accept the warning and suppress narrowly with `@SuppressWarnings({"unchecked", "varargs"})` plus a justifying comment — though `@SafeVarargs` is preferred because it also cleans up the call sites. ## Mental checklist before annotating 1. Does the body only read the varargs elements? 2. Does the array reference never escape the method? 3. Is the method static/final/private/constructor? If all three are yes, `@SafeVarargs` is appropriate. Otherwise, fix the design.

  • Why can't @SafeVarargs be applied to a non-final, overridable instance method?
    Because a subclass could override it with an unsafe implementation that the parent's annotation would falsely declare safe. Restricting it to static/final/private methods and constructors guarantees the annotated body is the one that runs.
  • What is a concrete example of an unsafe generic varargs body that should NOT be annotated?
    One that returns the varargs array (`return args`) or stores it in a field — the leaked array can then be polluted or read with the wrong type, causing a ClassCastException despite the annotation.
  • What is the difference between @SafeVarargs and @SuppressWarnings("unchecked")?
    @SafeVarargs is specific to generic varargs and suppresses the warning at both the declaration and all call sites; @SuppressWarnings("unchecked") is general and only affects where it is placed, not the callers.

saying these in an interview costs you the question

  • Claiming @SafeVarargs adds runtime type checks or makes the method safe
  • Putting it on a public, overridable instance method
  • Using it on a method that returns or stores the varargs array
  • Thinking it changes how the varargs array is created

context