Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding unexpected behaviors of varargs in Java

Tags:

java

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?

like image 561
anir Avatar asked Nov 13 '18 13:11

anir


1 Answers

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[][]).

like image 180
Eran Avatar answered Nov 15 '22 22:11

Eran