PROPOSAL: Simplified Varargs Method Invocation (Round II)

Bob Lee crazybob at
Sun Mar 8 15:29:45 PDT 2009

Simplified Varargs Method Invocation



FEATURE SUMMARY: When a programmer tries to invoke a varargs (variable
arity) method with a non-reifiable varargs type, the compiler currently
generates an "unsafe operation" warning. This proposal moves the warning
from the call site to the method declaration.

MAJOR ADVANTAGE: Safely and significantly reduces the total number of
warnings reported to and suppressed by programmers. Reduces programmer
confusion. Enables API designers to use varargs with non-reifiable types.

  - Plugs a leaky abstraction. Creating an array when you call a varargs
method is an implementation detail that we needn't expose to users.
  - Most programmers are surprised to find out they can only clear this
warning by suppressing it. They expect two language features introduced in
the same version to work well together. Programmers will no longer waste
time looking for alternatives that don't exist.
  - Google Code Search finds almost 90k callers of Arrays.asList() ( We can safely suppress the warning once and for
all on asList()'s declaration instead of unnecessarily warning every caller
that uses a non-reifiable type.

MAJOR DISADVANTAGE: The compiler will generate a warning for a method
declaration whether or not someone actually calls the method with a
non-reifiable type. Allows loss of type safety if the varargs method
suppresses the warning and uses the varargs array unsafely. Introduces a
small risk of not reporting warnings if you compile against code compiled
with Java 7 but run against code compiled with an earlier version.

  a) Don't mix varargs with generics. Use the more verbose and less
straightforward but warning-free builder pattern. Most API designers choose
this route.
  b) Improve the warning message. Current message: "uses unchecked or unsafe
  c) Reify generics.
  d) Introduce a second varargs syntax (perhaps using "...." instead of
"...") that uses List<T> instead of T[].
  e) Defile the type system.
  f) List literals: [a, b, c] instead of Arrays.asList(a, b, c)

Note: This proposal doesn't preclude any of these other approaches.



Before this change:

  static <T> List<T> asList(T... elements) { ... }

  static List<Callable<String>> stringFactories() {
    Callable<String> a, b, c;
    // Warning: "uses unchecked or unsafe operations"
    return asList(a, b, c);

After this change:

  // Warning: "enables unsafe generic array creation"
  static <T> List<T> asList(T... elements) { ... }

  static List<Callable<String>> stringFactories() {
    Callable<String> a, b, c;
    return asList(a, b, c);

If asList() prohibits storing elements that aren't of type T in the elements
array, we can safely suppress the warning:

  // Ensures only values of type T can be stored in elements.
  static <T> List<T> asList(T... elements) { ... }


Before this change:

  interface Sink<T> {
    void add(T... a);

  interface BrokenSink<T> extends Sink<T> {
    /** Overrides Sink.add(T...). */
    void add(T[] a); // no varargs

This code compiles and runs:

    BrokenSink<String> s = ...;
    s.add("a", "b", "c"); // varargs

A BrokenSink implementation could do something unsafe to the T[], so after
this change, we generate a warning:

  interface BrokenSink<T> extends Sink<T> {
    // Warning: "Overriddes non-reifiable varargs type with array"
    void add(T[] a);

To clear the warning, the programmer should use varargs instead of an array:

  interface BrokenSink<T> extends Sink<T> {
    void add(T... a);


The following method adds a non-null element to a Sink. Before this
proposal, the call to Sink.add() generates a warning:

  static <T> void addUnlessNull(Sink<T> sink, T t) {
    if (t != null)
      // Warning: "uses unchecked or unsafe operations"

Type T is unknown, so the method creates a single-element Object[]
containing t and passes it to Sink.add().

After erasure, this String-based implementation of Sink produces a more
narrow erased varargs type, specifically String[] instead of the Object[]
accepted by Sink.add():

  class StringSink implements Sink<String> {
    public void add(String... a) { ... }

If you run the following code, the bridge method StringSink.add(Object[])
will throw a ClassCastException when it tries to cast the Object[] created
in addUnlessNull() to the String[] required by StringSink.add(String[]):

    Sink<String> ss = new StringSink();
    addUnlessNull(ss, "Bob"); // ClassCastException!

After this change, the compiler warning moves to the vargs method
declaration that results in a more narrow erased array type:

  static <T> void addUnlessNull(Sink<T> sink, T t) {
    if (t != null)
      sink.add(t); // no warning

  class StringSink implements Sink<String> {
    // Warning: "override generates a more specific varargs type erasure"
    public void add(String... a) {}

At this point, the API designer would likely choose a different design
without an overridable varargs method. For example:

  interface Sink<T> {
    void add(T t); // no varargs

  static <T> void addNonNull(Sink<T> sink, T... a) {
    for (T t : a)
      if (t != null)


  - A varargs type is reifiable if its component type is reifiable (see JLS
  - When compiling code that calls a varargs method with a non-reifiable
varargs type, if the target method was compiled with Java 7 or later, the
compiler needn't generate a warning for the caller. (Note: This won't help
if you then run against code compiled with an earlier version, but the risk
is very low.)
  - When compiling a varargs method that could accept a non-reifiable
varargs type, the compiler should generate a warning on the varargs method
  - If a non-varargs method overrides a varargs method with a non-reifiable
varargs type, generate a warning. The warning a) makes it clear that the
method can still be used like a varargs method, and b) warns the implementor
to prevent unsafe operations on the array.
  - If a varargs method with a non-reifiable varargs type overrides another
varargs method and the varargs parameter erases to a more-specific array
type than that of the overridden method, generate a warning. If a client
invokes the sub type implementation through the super type's interface, the
client may pass in a super type of the array type expected by the sub type's
method which will result in a ClassCastException at run time.
  - For a varargs argument with a component type T, the programmer can
safely suppress the warning using @SuppressWarnings("generic-varargs") so
long as the varargs method ensures that only elements of type T can be
stored in the varargs array.

COMPILATION: Tools should no longer generate a warning for varargs method
callers. Instead, they should generate a warning on certain varargs method

TESTING: Compile test programs and ensure that the compiler generates the
expected warnings.

LIBRARY SUPPORT: Suppress warnings on the following varargs methods in the
  - Arrays.asList(T... a)
  - Collections.addAll(Collection<? super T> c, T... elements)
  - EnumSet.of(E first, E... rest)



MIGRATION: Existing callers may be able to remove
@SuppressWarnings("unchecked") from their code. Existing libraries should
add @SuppressWarnings("generic-varargs") to methods with signatures
containing non-reifiable varargs types.



EXISTING PROGRAMS: If you recompile an existing program with "-target 7",
the compiler will generate warnings for method declarations containing
non-reifiable varargs types.


Bug #5048776: "promote varargs/non-varargs overrider diagnostics to errors"

Bug #6227971: "generic inferrence bug in varargs"

JLS Section "Evaluate Arguments" (

"If the method being invoked is a variable arity method (§8.4.1) m, it
necessarily has n>0 formal parameters. The final formal parameter of m
necessarily has type T[] for some T, and m is necessarily being invoked with
k0 actual argument expressions.

If m is being invoked with kn actual argument expressions, or, if m is being
invoked with k=n actual argument expressions and the type of the kth
argument expression is not assignment compatible with T[], then the argument
list (e1, ... , en-1, en, ...ek) is evaluated as if it were written as (e1,
..., en-1, new T[]{en, ..., ek}).

The argument expressions (possibly rewritten as described above) are now
evaluated to yield argument values. Each argument value corresponds to
exactly one of the method's n formal parameters."

Angelika Langer's Java Generics FAQ, "Why does the compiler sometimes issue
an unchecked warning when I invoke a 'varargs' method?" (

Josh Bloch's "Effective Java" 2nd Edition, page 120 (

Alex Miller's blog, "Generics puzzler - array construction" (

"Java Generic and Collections", page 95 (

More information about the coin-dev mailing list