I was learning Java about how varargs works with the generic types. There was this function when I tried to return an array of generic types, which in this case T
is String
static <T> T[] toArr(T... listObj){
System.out.println("To arr");
System.out.println(listObj.getClass().getName());
System.out.println(listObj[0].getClass().getName());
return listObj;
}
static <T> T[] getArr1(T a1, T a2){
System.out.println("Get arr 1");
System.out.println(a1.getClass().getName());
return toArr(a1, a2);
}
public static void main(String[] args) {
String[] res1 = getArr1("one", "two");
}
And the output was this
Get arr 1
java.lang.String
To arr
[Ljava.lang.Object;
java.lang.String
//Error: casting from [Ljava.lang.Object to [Ljava.lang.String
From my understanding, while 2 String
objects were passed from getArr1
to toArr
, the array storing them were treated as Object
array, or T = Object
. When receiving the return from toArr
(Object[]
), the failure to convert to String[]
at getArr1
throw the exception.
Thing went differently when I explicitly declare the type with method getArr2
.
static String[] getArr2(String a1, String a2){
System.out.println("Get arr 2");
System.out.println(a1.getClass().getName());
return toArr(a1, a2);
}
Which outputs to
Get arr 2
java.lang.String
To arr
[Ljava.lang.String;
java.lang.String
Went well, no error at all. The varargs were treated as String[]
May I get some explanation how varargs was treated differently given the same input type as above cases?
First, varargs are just syntax sugar for "creating an array of the parameters just before the method is called". That is, a simple varargs method call like this:
method("foo", "bar");
// ...
public static void method(String... params) { }
is syntactic sugar for:
String[] params = new String[] { "foo", "bar" };
method(params);
// ...
public static void method(String[] params) { }
Second, you cannot create arrays of T
(generic type parameters). new T[]
is not allowed. This is because T
does not exist at runtime. The runtime does not know what T
is. See also: Java generics type erasure: when and what happens?
So what happens at the callsite of toArr
?
return toArr(a1, a2);
well, a1
and a2
are both T
s here, but we can't do this:
T[] params = new T[] { a1, a2 };
return toArr(params);
The compiler can only create an Object[]
here to store the parameters:
Object[] params = new Object[] { a1, a2 };
return toArr(params);
On the other hand, in getArr2
, a1
and a2
are String
s, so the compiler knows that it can create a String[]
to store them.