Skip to content
On this page

Generics and Reflection

Some parts of Java's reflection API are also generics. For example, Class<T> is generic:

java
// compile warning:
Class clazz = String.class;
String str = (String) clazz.newInstance();

// no warning:
Class<String> clazz = String.class;
String str = clazz.newInstance();

The getSuperclass() method of Class returns a Class type of Class<? super T>:

java
Class<? super String> sup = String.class.getSuperclass();

The Constructor<T> is also generic:

java
Class<Integer> clazz = Integer.class;
Constructor<Integer> cons = clazz.getConstructor(int.class);
Integer i = cons.newInstance(123);

You can declare arrays with generics, but you cannot create them using the new operator:

java
Pair<String>[] ps = null; // ok
Pair<String>[] ps = new Pair<String>[2]; // compile error!

You must use a cast to create arrays with generics:

java
@SuppressWarnings("unchecked")
Pair<String>[] ps = (Pair<String>[]) new Pair[2];

Be particularly cautious when using generic arrays, as arrays do not actually maintain generics at runtime. The compiler can enforce checks on the variable ps because it is a generic array, but it does not check the variable arr since it is not a generic array. Since these two variables actually point to the same array, manipulating arr could cause an error when retrieving elements from ps. The following code demonstrates unsafe usage of a generic array:

java
Pair[] arr = new Pair[2];
Pair<String>[] ps = (Pair<String>[]) arr;

ps[0] = new Pair<String>("a", "b");
arr[1] = new Pair<Integer>(1, 2);

// ClassCastException:
Pair<String> p = ps[1];
String s = p.getFirst();

To use generic arrays safely, you must eliminate the reference to arr:

java
@SuppressWarnings("unchecked")
Pair<String>[] ps = (Pair<String>[]) new Pair[2];

In the above code, without access to the original array reference, you can only operate on the generic array ps, making this operation safe.

Generic arrays effectively demonstrate the compiler's type erasure:

java
Pair[] arr = new Pair[2];
Pair<String>[] ps = (Pair<String>[]) arr;

System.out.println(ps.getClass() == Pair[].class); // true

String s1 = (String) arr[0].getFirst();
String s2 = ps[0].getFirst();

Therefore, we cannot directly create a generic array T[] because, after erasure, the code becomes Object[]:

java
// compile error:
public class Abc<T> {
    T[] createArray() {
        return new T[5];
    }
}

You must use Class<T> to create generic arrays:

java
T[] createArray(Class<T> cls) {
    return (T[]) Array.newInstance(cls, 5);
}

You can also create generic arrays T[] using varargs:

java
public class ArrayHelper {
    @SafeVarargs
    static <T> T[] asArray(T... objs) {
        return objs;
    }
}

String[] ss = ArrayHelper.asArray("a", "b", "c");
Integer[] ns = ArrayHelper.asArray(1, 2, 3);

Caution with Generic Varargs

In the example above, it seems safe to create a generic array using:

java
static <T> T[] asArray(T... objs) {
    return objs;
}

However, this method is very dangerous. The following code comes from an example in "Effective Java":

java
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] arr = asArray("one", "two", "three");
        System.out.println(Arrays.toString(arr));
        // ClassCastException:
        String[] firstTwo = pickTwo("one", "two", "three");
        System.out.println(Arrays.toString(firstTwo));
    }

    static <K> K[] pickTwo(K k1, K k2, K k3) {
        return asArray(k1, k2);
    }

    static <T> T[] asArray(T... objs) {
        return objs;
    }
}

Directly calling asArray(T...) seems fine, but returning a generic array from another method leads to ClassCastException. This is due to type erasure; within the pickTwo() method, the compiler cannot detect the correct type of K[], so it returns Object[].

If you look closely, you will notice that the compiler issues warnings for all varargs of generic types, and you should only suppress these warnings with @SafeVarargs if you are absolutely certain there are no issues.

Note

If you create a generic array within a method, it's best not to return it for external use.

For a more detailed explanation, refer to "Effective Java" in "Item 32: Combine generics and varargs judiciously."

Summary

  • Some reflection APIs are generics, such as Class<T> and Constructor<T>.
  • You can declare arrays with generics but cannot directly create them; you must use a cast.
  • You can create T[] arrays using Array.newInstance(Class<T>, int) with a cast.
  • Be especially cautious when using generics and varargs together.
Generics and Reflection has loaded