13

I was reading this answer, where it says

Note also that calling a generic vararg method with an explicit array parameter may silently produce different behaviour than expected:

public <T> void foo(T... params) { ... }
int[] arr = {1, 2, 3};
foo(arr); // passes an int[][] array containing a single int[] element

Similar behavior is exaplained in this answer gotcha 3:

int[] myNumbers = { 1, 2, 3 };
System.out.println(ezFormat(myNumbers));
// prints "[ [I@13c5982 ]"

Varargs only works with reference types. Autoboxing does not apply to array of primitives. The following works:

Integer[] myNumbers = { 1, 2, 3 };
System.out.println(ezFormat(myNumbers));
// prints "[ 1 ][ 2 ][ 3 ]"

I tried simpler examples:

private static <T> void tVarargs(T ... s)
{
    System.out.println("\n\ntVarargs ==========");
    System.out.println(s.getClass().getName());
    System.out.println(s.length);
    for(T i : s)
        System.out.print(s + ",");
}

private static void objVarargs(Object ... a)
{
    System.out.println("\n\nobjVarargs =========== ");
    System.out.println(a.getClass().getName());
    System.out.println(a.length);
    for(Object i : a)
        System.out.print(i + ",");
}

int[] intarr = {1,2,3}; 
Integer[] Intarr = {1,2,3};

objVarargs(intarr);
objVarargs(Intarr);

tVarargs(intarr);
tVarargs(Intarr);

This prints

objVarargs =========== 
[Ljava.lang.Object;
1
[I@7852e922,

objVarargs =========== 
[Ljava.lang.Integer;
3
1,2,3,

tVarargs ==========
[[I
1
[[I@4e25154f,

tVarargs ==========
[Ljava.lang.Integer;
3
[Ljava.lang.Integer;@70dea4e,[Ljava.lang.Integer;@70dea4e,[Ljava.lang.Integer;@70dea4e,
  • Notice passing intarr to tVarargs results in creation of single 2 dimensional array [[I wit single element. However, what is the types of this array?
  • Also, passing intarr to objVarargs() results in creation of 1-D array [Ljava.lang.Object containing single array element.
  • For rest, it creates 1-D array with as many elements as passed - the desired behavior.

Can someone shed more light on first two behaviors? Are these two different behaviors or same behaviors, I mean having different or same reason behind them. What are those reasons, in depth? Is there any other cases resulting in different unexpected behavior?

MsA
  • 2,599
  • 3
  • 22
  • 47
  • I am not sure what is confusing you here. `Type...` is just `Type[]` but allows us to pass arguments of `Type` as list instead of wrapping it into array manually. `` is limited to reference types only (it can't represent primitive type like `int`) which means `T...` as `T[]` can't be `int[]`. If you pass `int[]` array then most specific object type which `T` can use is array itself. So since `T` is now `int[]` `T...` is like `int[]...` which results in `int[][]` (that is what `[[I` represents). `Object...` is simply `Object[]` array and that is what you see in other method – Pshemo Nov 13 '18 at 14:05

1 Answers1

5

These two behaviors result from the same issue - both generic type parameters and Object variables can only hold reference types. A primitive (such as int) is not a reference type, so passing an int array to a varargs method (and it doesn't matter if it's Object ... a or T ... s) results in the method accepting an array having a single element, and that single element is the int array.

Therefore you can consider it as a 2 dimensional int array (i.e. int[][]) containing a single row.

Note that you have a typo in tVarargs(T ... s) which results in confusing output. It should be System.out.print(i + ",");, not System.out.print(s + ",");

Once you fix that, both methods produce the same output for the Integer[] input:

[Ljava.lang.Integer;
3
1,2,3,

The difference in the outputs for the int[] input results from the fact that in objVarargs(Object ... a), the type of the varargs array is Object[], and in tVarargs(T ... s) it's T[] (and when T is an int[], the type of the array is an int[][]).

BTW, [[I is the class name of a two dimensional int array (i.e. int[][]).

Eran
  • 387,369
  • 54
  • 702
  • 768