Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
In Java, three ASCII periods (...) declare a variable-arity parameter, usually called varargs. They are not a generics operator or wildcard. A declaration such as <T> void print(T... values) combines a generic type parameter with varargs; the generic type is T, and the ellipsis lets callers supply zero or more values. Generic varargs need care because their array-like representation can produce unchecked warnings and, if misused, heap pollution.
One important distinction: the typographic ellipsis … (U+2026) is not Java syntax. Java source uses three separate ASCII periods: ....
What does ... mean in Java?
In a method declaration, ... marks the final parameter as variable-arity: callers may provide zero or more arguments of that parameter’s element type.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →static void printAll(String... values) {
for (String value : values) {
System.out.println(value);
}
}
These calls are valid:
printAll(); // zero values
printAll("A", "B", "C"); // separate values
String[] names = {"A", "B"};
printAll(names); // an existing array
Inside the method, values behaves like an array: it has a length, can be indexed, and can be traversed with an enhanced for loop. A varargs parameter must be last, so void okay(String prefix, int... values) is valid, but void notOkay(int... values, String suffix) is not. The Java Language Specification’s variable-arity rules define the declaration form.
Varargs are array-like, but they are not identical to an ordinary array parameter at the call site:
static void varargs(String... values) {}
static void arrayOnly(String[] values) {}
varargs("A", "B"); // valid
arrayOnly("A", "B"); // compile-time error
String[] values = {"A", "B"};
varargs(values); // valid
arrayOnly(values); // valid
A varargs call with separate arguments packages them into an array for the method. If you already have an array, you can pass it directly. The method-invocation rules also treat variable-arity applicability as a distinct stage of overload resolution.
How varargs combines with generics
A generic method can use a type variable as its varargs element type:
static <T> void print(T... values) {
for (T value : values) {
System.out.println(value);
}
}
print("one", "two");
print(1, 2, 3);
print(List.of("A"), List.of("B"));
Here <T> declares the type variable. The compiler can infer a suitable T from a call. T... says the method accepts a variable number of values of that type. It is conceptually similar to a final T[] parameter, but unlike an array-only parameter, it permits separate arguments.
The same combination can appear in a generic class:
Rank #2
class Collector<T> {
void collect(T... values) {
for (T value : values) {
System.out.println(value);
}
}
}
The ellipsis does not declare or infer T; it controls how many arguments a caller may supply. For a broader overview of Java type parameters, inference, wildcards, and erasure, see Dev.java’s generics guide.
Why generic varargs can trigger warnings
Consider a varargs parameter whose element type is parameterized:
Recommended Free Tools
static void addLists(List<String>... lists) {
for (List<String> list : lists) {
System.out.println(list);
}
}
A compiler commonly warns that this declaration may cause heap pollution. List<String> is non-reifiable: after type erasure, the runtime does not retain the String type argument in a way that lets an array check enforce it. Arrays, by contrast, carry their component type at runtime. Java generally cannot create an array whose runtime component type is a parameterized type such as List<String>. See the JLS sections on reifiable types and type erasure.
Heap pollution means that a variable of a parameterized type refers to an object that does not satisfy the type argument its code assumes. The problem can be introduced at a varargs boundary and only become visible later:
static void unsafe(List<String>... lists) {
Object[] array = lists;
array[0] = List.of(42); // generic mismatch is not checked here
String value = lists[0].get(0); // may fail when the value is used
}
This is deliberately unsafe. The warning does not mean every generic varargs method is broken; it signals that the array-like boundary cannot fully verify the generic type at runtime. Risk increases if code writes to the array, returns it, stores it, or exposes it to code that may retain or mutate it. A later ClassCastException may occur far from the point where the bad value entered.
When @SafeVarargs is appropriate
@SafeVarargs tells the compiler that the author has reviewed a varargs method or constructor and asserts that its use of the parameter is safe. It suppresses the relevant warning; it does not make unsafe code safe. Under the current language rules, it may annotate a static, final, or private method, or a constructor.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall@SafeVarargs
static <T> void print(T... values) {
for (T value : values) {
System.out.println(value);
}
}
This read-only implementation uses the elements without modifying or exposing the array. Before adding the annotation, check that the method does not write incompatible values into the array, return or store it where it could be misused, or hand it to code that may retain or mutate it. An annotation that merely hides a warning is not a safety fix. The Java API documentation for SafeVarargs describes its purpose and restrictions.
How the common symbols differ
| Syntax | Meaning | Example |
|---|---|---|
<T> |
Declares a type variable on a generic method or type. | <T> void use(T item) |
List<T> |
A parameterized type using type variable T. |
List<String> |
? |
A wildcard: an unknown type argument. | List<?> |
? extends T |
A wildcard with an upper bound. | List<? extends Number> |
? super T |
A wildcard with a lower bound. | List<? super Integer> |
<> |
The diamond operator, which lets the compiler infer constructor type arguments. | new ArrayList<>() |
... |
Marks a variable-arity parameter. | String... values |
[] |
Array declaration or access syntax. | String[] values |
A wildcard and varargs answer different questions. In List<?>, ? means “a list of some unknown element type.” In String..., the ellipsis means “zero or more strings.” They can appear together:
static void printLists(List<?>... lists) {
for (List<?> list : lists) {
System.out.println(list);
}
}
Do not assume that adding a wildcard automatically removes every warning; the exact parameter type and varargs declaration matter. Also, List<Object> is not the same as List<?>: the former is a list whose element type is specifically Object, while the latter can refer to a list with any element type. See Oracle’s explanation of unbounded wildcards.
Common traps and alternatives
Generic array creation
These declarations are illegal because Java cannot create arrays with a non-reifiable component type:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Rank #4
// T[] values = new T[10];
// List<String>[] lists = new List<String>[10];
Use a collection when you need storage that grows or holds generic values:
List<T> values = new ArrayList<>(10);
When an actual array is required, a caller-supplied array factory can preserve the runtime component type:
static <T> T[] create(int size, IntFunction<T[]> factory) {
return factory.apply(size);
}
String[] names = create(10, String[]::new);
A cast from new Object[10] to T[] is not automatically safe; use it only when a carefully maintained invariant justifies it. The JLS explains which types are reifiable in its type rules.
Null is not the same as no arguments
printAll(); // an empty varargs array
printAll((String) null); // one null element
printAll((String[]) null); // a null array reference
The last call passes a null reference, so code that iterates the parameter without checking may throw NullPointerException. If the API permits that call, handle it deliberately. An uncast printAll(null) can be confusing, particularly when overloads are involved; an explicit cast makes the intended call clearer.
Overloads can change which method is called
static void log(String value) {
System.out.println("single");
}
static void log(String... values) {
System.out.println("varargs");
}
log("one"); // selects the fixed-arity overload
Java considers applicable fixed-arity methods before variable-arity invocation. Adding a varargs overload to an existing API can therefore change behavior or create ambiguity for some calls, especially around null, boxing, widening, and generic inference. For complex overload sets, consult the JLS rules for determining applicability and choosing the most specific method.
Best Value
Choose the parameter shape that fits the input
| Parameter | Use it when | Trade-off |
|---|---|---|
T... values |
The method naturally accepts a convenient zero-or-more list of arguments. | Call-site friendly, but generic element types can trigger warnings; callers can also pass a null array. |
T[] values |
The method specifically requires an array, often because the caller already has one. | Explicit array contract; callers cannot supply separate values. |
List<T> values |
The input is conceptually a collection, or the method needs collection operations. | A collection avoids the generic-array boundary, but callers pass a collection rather than separate arguments. |
List<?> values |
The method can work with a list without knowing its element type. | Useful for read-only or type-agnostic operations; the wildcard does not promise elements of a particular type. |
For example, if the values are already a group of lists, a collection parameter is often clearer and avoids the generic-varargs boundary:
static <T> void process(List<List<T>> groups) {
for (List<T> group : groups) {
// process each group
}
}
process(List.of(List.of("A", "B"), List.of("C")));
Use bounded wildcards when the method needs a type relationship rather than a variable argument count. For example, List<? extends Number> can supply numbers to a read-only summing method. The teaching mnemonic “producer extends, consumer super” can help when designing bounded-wildcard APIs, but it is a design aid rather than a separate Java language rule.
What to do about a heap-pollution warning
- Inspect whether the method mutates, stores, returns, or exposes its varargs array.
- If it does, decide whether the API can instead accept a collection or an explicitly typed array.
- If the method only reads values and its safety can be justified, consider
@SafeVarargswhere the declaration is eligible. - Keep the safety argument tied to the implementation: later changes that expose or mutate the array may invalidate it.
- Do not suppress unrelated unchecked warnings simply to make compiler output quiet.
In short: ... controls argument count; generics control type relationships. They can be combined, but when the element type is non-reifiable, treat the resulting warning as a request to inspect the array boundary, not as a message to silence automatically.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsFor current language rules, use the Java SE 26 edition of the JLS. Oracle notes that its classic generics tutorial was written for JDK 8; it remains useful for examples, while the current specification is the normative source for language rules.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

