A generic varargs parameter is represented by an array whose runtime component type cannot fully represent its parameterized element type.
Java generic varargs: audit the hidden array before using SafeVarargs
Keep the temporary array private
A method taking List<? extends T>... receives an array of lists. If it writes an incompatible list through an Object[] alias or returns that array to a caller, a later read can fail far from the write. The checked method only reads each list and copies its values into a fresh result. It does not store the array or expose it.
@SafeVarargs is an assertion by the method author, not a compiler proof that arbitrary body code is safe. In Java 8 it may annotate a static or final method. Audit any later edit to the method before keeping the annotation. Erasure and bounds and array call boundaries supply the two rules behind this case.
Decide how much to copy
The result is a new List, but its values are the same referenced objects from the inputs. That is enough for immutable String dispatch IDs. If the inputs contain mutable orders, a caller cannot claim a deep snapshot. Array copy ownership shows the same distinction in a simpler container.
Reject a null batch rather than letting an incidental NullPointerException surface inside a traversal. A live input list modified concurrently has no stable snapshot guarantee; copy or synchronize at the caller boundary.
Working program
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DispatchBatchUnion {
@SafeVarargs
static <T> List<T> snapshot(List<? extends T>... batches) {
List<T> result = new ArrayList<>();
for (List<? extends T> batch : batches) {
if (batch == null) throw new IllegalArgumentException("null batch");
result.addAll(batch);
}
return result;
}
public static void main(String[] args) {
List<String> north = Arrays.asList("dispatch-47", "dispatch-82");
List<String> south = Arrays.asList("dispatch-15");
System.out.println(snapshot(north, south));
}
}Output
[dispatch-47, dispatch-82, dispatch-15]Cost and ownership
Copying k references across all batches takes O(k) time and O(k) result storage. The hidden varargs array is short-lived and not returned. The method does not copy or freeze the elements, and result allocation can still fail for very large inputs.
Common Mistakes
- Do not annotate a varargs method as safe after it writes through an Object[] alias.
- Do not return or retain the varargs array as a supposedly typed collection.
- Do not mistake a new outer List for copies of mutable elements.
Read next
Java generics: invariance, bounds, and type erasure, Java varargs: arrays at a method boundary, Java array copies: new slots can still point at old objects, Java collection views: live wrappers, snapshots and shallow copies.
