Indeed add(E)
is not appliccable, but the matter is less clear for the method add(E...)
:
The Java Language Specification defines:
The method m is an applicable variable-arity method if and only if all of the following conditions hold:
For 1 ≤ i < n, the type of ei, Ai, can be converted by method invocation conversion to Si.
If k ≥ n, then for n ≤ i ≤ k, the type of ei, Ai, can be converted by method invocation conversion to the component type of Sn.
...
In our case Sn is capture-of-? extends Number[]
.
Now just what is the component type of Sn? Unfortunately, the JLS does not give a formal definition for that term. In particular, it not said explicitly whether the component type is a compile time type (which need not be reifiable) or a runtime type (which must be). In case of the former, the component type would be capture-or-? extends Number
, which cannot accept a Number through method invocation conversion. In case of the latter, the component type would be Number
, which obviously can.
It appears that the implementors of the eclipse compiler have used the former definition, while the implementors of javac have used the latter.
Two facts seem to imply that the component type is indeed a runtime type. First, the spec requires:
If the type of the value being assigned is not assignment-compatible (§5.2) with the component type, an ArrayStoreException is thrown.
If the component type of an array were not reifiable (§4.7), the Java Virtual Machine could not perform the store check described in the preceding paragraph. This is why an array creation expression with a non-reifiable element type is forbidden (§15.10). One may declare a variable of an array type whose element type is non-reifiable, but assignment of the result of an array creation expression to the variable will necessarily cause an unchecked warning (§5.1.9).
and second, the runtime evaluation of a method invocation expression targeting a variable arity method is defined as follows:
If m is being invoked with k ≠ n actual argument expressions, or, if m is being invoked with k = n actual argument expressions and the type of the k'th 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 }), where |T[]| denotes the erasure (§4.6) of T[].
A casual reading of this paragraph might lead one to think that the expression is not just evaluated, but also type checked that way, but the specification doesn't quite say that.
On the other hand, it is quite an odd notion to use the erasure for compile time type checking (though the spec requires this in some other cases).
To summarize, the observed differences between the eclipse compiler and javac appear to stem from a slightly different interpretation of the type checking rules for non-reifiable variable-arity method arguments.