If I write the Java method
public static void f(int... x) {
for (int a: x) {
System.out.println(a);
}
}
then I can call this method via
f(1, 2, 3);
as well as
f(new int[]{1, 2, 3});
and both calls are treated exactly the same. However, the two calls
Arrays.asList(1, 2, 3) // (a) produces a three-element Integer list
and
Arrays.asList(new int[]{1, 2, 3}) // (b) produces a one-element list of Integer arrays
are not treated the same. The section on evaluating arguments in the JLS states "The final formal parameter of m necessarily has type T[]
for some T
" so why isn't the "final formal parameter" in case (b) above not an integer array which would create a three-element list, like case (a)?
The only thing I can think of is that Arrays.asList
is generic, and something about type compatibility (mentioned in JLS Section 15.12.4.2) is at play here, but I can't quite put my finger on the actual sentence in the JLS that makes the two cases different.
The commentary in the JLS talks about the need to craft these semantics carefully in order to handle the interplay between "parameterized types and array types that occurs in a Java Virtual Machine with erased generics" but what we end up is my function f
and Arrays.asList
appearing to behave inconsistent. In the former case I can "unpack" or "pack" my args and all is the same; in the latter case, the two cases are different.
How is this explained in language-lawyer terms?
Varargs is a short name for variable arguments. In Java, an argument of a method can accept arbitrary number of values. This argument that can accept variable number of values is called varargs. The syntax for implementing varargs is as follows: accessModifier methodName(datatype… arg) { // method body }
Varargs are useful for any method that needs to deal with an indeterminate number of objects. One good example is String. format . The format string can accept any number of parameters, so you need a mechanism to pass in any number of objects.
There is no limit. There can be any number of methods or constructors with one Vararg per each method or constructor.
Arrays.asList
is defined as having parameter T...
, and the body will see it as one argument of type T[]
. However, T
must be a reference type (like any type parameter), and int
is not a reference type. Because of that, there's no way to make new int[]{...}
compatible with an array for varargs purposes. (And the result would be List<int>
which isn't allowed.) The only alternative is to treat it as a single object, and thus T
here will be int[]
, which is a reference type, and the function will return a List<int[]>
.
I think that's the reason, although I would still need to look up the exact language. It probably involves the type inference rules, which I haven't mastered.
However, I did try this:
List x = Arrays.asList(new int[]{1, 2, 3});
List y = Arrays.asList(new Integer[]{1, 2, 3});
System.out.println(x.size());
System.out.println(y.size());
(note that I deliberately used a List
raw type, something I wouldn't do in real code). The output:
1
3
Arrays.asList(1, 2, 3);
1 2 3 are autoboxed to Integer objects which are then placed in an Object[]
Arrays.asList(new int[]{1, 2, 3});
forces the 3 integers into a primitive int array.
Arrays.asList()
expects Object[]
, so it places your int[]
array into a single element Object[]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With