5

1) OK

List<int[]> listOfArrays1 = Arrays.asList( new int[]{1, 2} );

2) OK

List<int[]> listOfArrays2 = Arrays.asList( new int[]{1, 2}, new int[]{3, 4} );

3) Compile error Type mismatch: cannot convert from List<Integer> to List<Integer[]>

List<Integer[]> listOfArrays3 = Arrays.asList( new Integer[]{1, 2} );

4) OK

List<Integer[]> listOfArrays4 = Arrays.asList( new Integer[]{1, 2},  new Integer[]{3, 4} );

This is the signature for asList: public static <T> List<T> asList(T... a)

asList expects 0 or more "a" of type T. My "a" is new Integer[]{1, 2} and it is of type Integer[]. So, why is it generating a List<Integer> instead of a List<Integer[]>?

Gustavo
  • 1,332
  • 2
  • 16
  • 39
  • Please explain how 1 and 2 didn't show any error. `List` doesn't work with primitive `int`. – Sufian Jul 15 '16 at 13:48
  • 2
    @Sufian `int[]` is not primitive. It's an object. – RaminS Jul 15 '16 at 13:48
  • (1) resolves correctly is because `T` can be `int[]` but not `int`. Related http://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types – blgt Jul 15 '16 at 13:55
  • OP, I suggest you read the answers in the chain of duplicate questions that have been marked here and determine if they are at all satisfactory (I don't think they are). If not, edit your question and at the top explain why you think this is not a duplicate. Maybe then the mark will be removed. – Zircon Jul 15 '16 at 15:07

1 Answers1

3

Let's look at the problem example (3rd):

List<Integer[]> listOfArrays3 = Arrays.asList( new Integer[]{1, 2} );

As you showed, the method signature is:

public static <T> List<T> asList(T... a)

In this particular case, the single Integer[] is being considered for the T.... An array or an unspecified number of the same object can be supplied to T.... Since you specified one array, the T is regarded as Integer (and T... becomes Integer[]).

When you supply an int[] as a single argument (1st), the compiler doesn't automatically wrap this to an Integer[] because such an object is different from an int[]. Because int is not an object, the only object type that can fit T is int[] (which builds the parameter as an int[][]).

Supplying two int[]s (2nd) is much more obvious, as the compiler can only regard two arrays for T... as int[]s, thus T... is also int[][].

When you supply two Integer[]s (4th), it is more obvious again that the compiler has no choice but to regard the two parameters which make up T... as Integer[] (which becomes a single array: Integer[][]).

Edit: Supplying an array as a vararg:

You can supply a single array as a vararg. Let's take an example without a generic:

public int iLoveMeSomeInts(int...nums)

Supplying an int[] to this method as an argument does work. The array is regarded as a vararg of ints for the purposes of validating the signature, then the vararg is regarded as an int[] for the method's internal logic.

The difference in your example is that the argument is T.... A generic T must be an object, so the compiler cannot regard an int[] as a vararg of int... in this case. The compiler then has no choice but to regard the int[] as a single element in a vararg of int[]... (because int[] is an object). There is no ambiguity in this.

However, because Integer is an object, the compiler will use a single Integer[] as Integer....

What's even cooler is this: if you wanted an Integer[] returned using the method in question, and still only supplied a single Integer[], you could call:

Arrays.<Integer[]>asList(new Integer[] {1, 2});

This forces the compiler to regard your single Integer[] as Integer[]....

Zircon
  • 4,677
  • 15
  • 32
  • I guess I didn't really know how varargs work. – Gustavo Jul 15 '16 at 14:00
  • 1
    varvargs are great, because you can supply any number of the same object, and in the method itself that argument is treated as an array. The gotcha here is that a generic `T` must be an object, so supplying an `int[]` will not count as a vararg of `int`s. – Zircon Jul 15 '16 at 14:02
  • So in other words (if I understood this correctly), case 3 is ambiguous because it can be interpreted as both two arguments `(1, 2)` and one argument `(new Integer[]{1,2})`. It happens to be the case that the compiler decides to choose the first one and if you are expecting the second outcome, you get surprised. – RaminS Jul 15 '16 at 14:04
  • Close, but it's not ambiguous at all. You _can_ supply a single array for varargs. I will clarify this for you in an edit of the answer. – Zircon Jul 15 '16 at 14:06
  • @Gendarme http://stackoverflow.com/a/2925300/2761418 – Gustavo Jul 15 '16 at 14:13
  • *"However, because `Integer` is an object, the compiler will use a single `Integer[]` as `Integer...`."* This seems rather arbitrary (perhaps ambiguous is the wrong word). The Java designers could have just as well decided to treat it as a `Integer[]...`, no? – RaminS Jul 15 '16 at 14:26
  • 1
    They could have, but it would be somewhat self-defeating. It's convenient to pass a single array as a vararg. If you had a full-and-ready `Integer[]` you wanted to pass as `Integer...`, it would be much more complicated (and not very intuitive) to get each element and pass them as separate arguments. In contrast, if you had a single `Integer[]` you wanted to pass as an `Integer[]...`, you could either force it via the statement i showed or, if you didn't know about it, pass the argument as `new Integer[][] { myIntegerArray }`. – Zircon Jul 15 '16 at 14:31
  • @Zircon In the signature `public static ** List asList(T... a)` I didn't get what the first `` mean. I think `Arrays.**asList(new Integer[] {1, 2});` explains. – Gustavo Jul 15 '16 at 15:42
  • The `` before the return type is declaring the generic `T`. This is used when a generic-typed method is used in a non-generic class. For example, if I make a method `public static T doSomething(T...nums)`, I am forcing `T...` to be a vararg of any object that is `instanceof Number`, meaning `Number` and its child classes. – Zircon Jul 15 '16 at 15:45
  • @Zircon Ok. I'm wrong. So, where does this `Arrays.asList...` come from? – Gustavo Jul 15 '16 at 16:01
  • @Gustavo You were right, though. Making that call means you are declaring `` to be `Integer[]`. – Zircon Jul 15 '16 at 17:10