Search code examples
javaarraysgenericsvariadic-functions

Return type of varargs and generics


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?


Solution

  • 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 Ts 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 Strings, so the compiler knows that it can create a String[] to store them.