Home > other >  Creating an array of generics in java in the method, without a class object
Creating an array of generics in java in the method, without a class object

Time:08-31

I am doing tasks from the Java SE 8 book by Cay S. Horstmann (Chapter 6 task 21)

Using the @SafeVarargs annotation, write a method that allows you to build arrays of generalized types, as in the following example:

List<String>[] result = Arrays.<List<String> > construct (10) ;
// Sets the result in a list of type List<String>[] with a length of 10

My intended solution was such

static <T> T[] construct(int size) {
    ArrayList<String> arr = new ArrayList<>();
    for (int i = 0; i < size; i  ) arr.add(null);

    return (T[]) arr.toArray();
}

but it is incorrect since after erasing occurs

Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.util.List; ([Ljava.lang.Object; and [Ljava.util.List; are in module java.base of loader 'bootstrap') at Main.main(Main.java:23)

CodePudding user response:

It's a common mistake. arr.toArray() returns Object[] and then you try to cast it to List[] array. Unlike generics the type of arrays is not removed during runtime. That's why you get a class cast exception.

CodePudding user response:

The @SafeVarargs does already provide a hint towards the solution. Make the method a varargs method. Since a variable number of arguments also allows zero arguments, this change allows unchanged invocations:

public class Example
{
  @SafeVarargs
  static <T> T[] construct(int size, T... template) {
      return Arrays.copyOf(template, size);
  }

  public static void main(String args[])
  {
    List<String>[] result = Example.<List<String>>construct(10);
    System.out.println(result.getClass().getComponentType());
  }
}

Note that you don’t need explicit type parameters here. The caller can be simplified to

List<String>[] result = Example.construct(10);

which implies that we can even omit the declaring class when being in the same class or using import static …

List<String>[] result = construct(10);

But when the caller does provide arguments to the varargs parameter, they may show up in the resulting array. To enforce an array with only null elements, you could use

@SafeVarargs
static <T> T[] construct(int size, T... template) {
    return Arrays.copyOf(Arrays.copyOf(template, 0), size);
}

or

@SafeVarargs
static <T> T[] construct(int size, T... template) {
    T[] array = Arrays.copyOf(template, size);
    Arrays.fill(array, null);
    return array;
}

which can be tested with

List<String>[] result = construct(10, Collections.emptyList());
System.out.println(result.getClass().getComponentType());
System.out.println(Arrays.toString(result));

But note that the creation of generic arrays is not allowed for a reason. Consider

List<String>[] result = construct(10);
Object[] array = result;
array[0] = Arrays.asList(42);
List<String> stringList = result[0];

// this situation is called heap pollution

// at an entirely different place in you application, you might do
String s = stringList.get(0);
// without understanding what's going on
  • Related