How to Create a Generic Array

How to create a generic array in Java?

I have to ask a question in return: is your GenSet "checked" or "unchecked"?
What does that mean?

  • Checked: strong typing. GenSet knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with a Class<E> argument, and methods will throw an exception when they are passed arguments that are not of type E. See Collections.checkedCollection.

    -> in that case, you should write:

    public class GenSet<E> {

    private E[] a;

    public GenSet(Class<E> c, int s) {
    // Use Array native method to create array
    // of a type only known at run time
    @SuppressWarnings("unchecked")
    final E[] a = (E[]) Array.newInstance(c, s);
    this.a = a;
    }

    E get(int i) {
    return a[i];
    }
    }
  • Unchecked: weak typing. No type checking is actually done on any of the objects passed as argument.

    -> in that case, you should write

    public class GenSet<E> {

    private Object[] a;

    public GenSet(int s) {
    a = new Object[s];
    }

    E get(int i) {
    @SuppressWarnings("unchecked")
    final E e = (E) a[i];
    return e;
    }
    }

    Note that the component type of the array should be the erasure of the type parameter:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo

    private Foo[] a; // E erases to Foo, so use Foo[]

    public GenSet(int s) {
    a = new Foo[s];
    }

    ...
    }

All of this results from a known, and deliberate, weakness of generics in Java: it was implemented using erasure, so "generic" classes don't know what type argument they were created with at run time, and therefore can not provide type-safety unless some explicit mechanism (type-checking) is implemented.

How to create a generic array?

You should not mix-up arrays and generics. They don't go well together. There are differences in how arrays and generic types enforce the type check. We say that arrays are reified, but generics are not. As a result of this, you see these differences working with arrays and generics.

Arrays are covariant, Generics are not:

What that means? You must be knowing by now that the following assignment is valid:

Object[] arr = new String[10];

Basically, an Object[] is a super type of String[], because Object is a super type of String. This is not true with generics. So, the following declaration is not valid, and won't compile:

List<Object> list = new ArrayList<String>(); // Will not compile.

Reason being, generics are invariant.

Enforcing Type Check:

Generics were introduced in Java to enforce stronger type check at compile time. As such, generic types don't have any type information at runtime due to type erasure. So, a List<String> has a static type of List<String> but a dynamic type of List.

However, arrays carry with them the runtime type information of the component type. At runtime, arrays use Array Store check to check whether you are inserting elements compatible with actual array type. So, the following code:

Object[] arr = new String[10];
arr[0] = new Integer(10);

will compile fine, but will fail at runtime, as a result of ArrayStoreCheck. With generics, this is not possible, as the compiler will try to prevent the runtime exception by providing compile time check, by avoiding creation of reference like this, as shown above.

So, what's the issue with Generic Array Creation?

Creation of array whose component type is either a type parameter, a concrete parameterized type or a bounded wildcard parameterized type, is type-unsafe.

Consider the code as below:

public <T> T[] getArray(int size) {
T[] arr = new T[size]; // Suppose this was allowed for the time being.
return arr;
}

Since the type of T is not known at runtime, the array created is actually an Object[]. So the above method at runtime will look like:

public Object[] getArray(int size) {
Object[] arr = new Object[size];
return arr;
}

Now, suppose you call this method as:

Integer[] arr = getArray(10);

Here's the problem. You have just assigned an Object[] to a reference of Integer[]. The above code will compile fine, but will fail at runtime.

That is why generic array creation is forbidden.

Why typecasting new Object[10] to E[] works?

Now your last doubt, why the below code works:

E[] elements = (E[]) new Object[10];

The above code have the same implications as explained above. If you notice, the compiler would be giving you an Unchecked Cast Warning there, as you are typecasting to an array of unknown component type. That means, the cast may fail at runtime. For e.g, if you have that code in the above method:

public <T> T[] getArray(int size) {
T[] arr = (T[])new Object[size];
return arr;
}

and you call invoke it like this:

String[] arr = getArray(10);

this will fail at runtime with a ClassCastException. So, no this way will not work always.

What about creating an array of type List<String>[]?

The issue is the same. Due to type erasure, a List<String>[] is nothing but a List[]. So, had the creation of such arrays allowed, let's see what could happen:

List<String>[] strlistarr = new List<String>[10];  // Won't compile. but just consider it
Object[] objarr = strlistarr; // this will be fine
objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.

Now the ArrayStoreCheck in the above case will succeed at runtime although that should have thrown an ArrayStoreException. That's because both List<String>[] and List<Integer>[] are compiled to List[] at runtime.

So can we create array of unbounded wildcard parameterized types?

Yes. The reason being, a List<?> is a reifiable type. And that makes sense, as there is no type associated at all. So there is nothing to loose as a result of type erasure. So, it is perfectly type-safe to create an array of such type.

List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>(); // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine

Both the above case is fine, because List<?> is super type of all the instantiation of the generic type List<E>. So, it won't issue an ArrayStoreException at runtime. The case is same with raw types array. As raw types are also reifiable types, you can create an array List[].

So, it goes like, you can only create an array of reifiable types, but not non-reifiable types. Note that, in all the above cases, declaration of array is fine, it's the creation of array with new operator, which gives issues. But, there is no point in declaring an array of those reference types, as they can't point to anything but null (Ignoring the unbounded types).

Is there any workaround for E[]?

Yes, you can create the array using Array#newInstance() method:

public <E> E[] getArray(Class<E> clazz, int size) {
@SuppressWarnings("unchecked")
E[] arr = (E[]) Array.newInstance(clazz, size);

return arr;
}

Typecast is needed because that method returns an Object. But you can be sure that it's a safe cast. So, you can even use @SuppressWarnings on that variable.

Cannot create generic array of .. - how to create an Array of Map String, Object ?

Because of how generics in Java work, you cannot directly create an array of a generic type (such as Map<String, Object>[]). Instead, you create an array of the raw type (Map[]) and cast it to Map<String, Object>[]. This will cause an unavoidable (but suppressible) compiler warning.

This should work for what you need:

Map<String, Object>[] myArray = (Map<String, Object>[]) new Map[10];

You may want to annotate the method this occurs in with @SuppressWarnings("unchecked"), to prevent the warning from being shown.

What's the reason I can't create generic array types in Java?

It's because Java's arrays (unlike generics) contain, at runtime, information about its component type. So you must know the component type when you create the array. Since you don't know what T is at runtime, you can't create the array.

Creating a generic array for CompletableFuture

There are several wrong assumptions in your question

  • You can’t do (CompletableFuture<?>[]) newReports.toArray(). The parameterless toArray() method will return Object[] and the casting attempt will cause a ClassCastException. Only the toArray(T[]) method accepting an existing array will return an array of the same type, which brings you back to square zero

  • CompletableFuture.allOf returns a CompletableFuture<Void>, so you can’t call get() on it and expect to get a List<List<ReportComparable>>. You have to assemble the result list yourself after completion.

  • There is no problem creating an array of a generic class, when all type arguments are wildcards. So

    CompletableFuture<?>[] array = new CompletableFuture<?>[100];

    works.

  • When you have an arbitrary number of elements, adding them to a List and invoking toArray on it, isn’t necessarily inefficient. The alternative, dealing with an array and an index manually, is doing the same as an ArrayList, but error prone. The single copying step of toArray is rarely performance relevant.

    Also keep in mind, since the CompletableFuture.allOf returns CompletableFuture<Void> you might need the List<CompletableFuture<List<ReportComparable>>> anyway to be able to construct the desired List<List<ReportComparable>> after the completion.

    On the other hand, when you have a fixed number of arguments, you may call the varargs method CompletableFuture.allOf directly without manual array creation.


But when all you want to do with the CompletableFuture returned by allOf, is to call get() immediately, the “wait for all” operation doesn’t have any benefit anyway.

You get the same effect implicitly when querying the individual CompletableFuture instances via get() or join() and adding the result to your resulting List, which you have to do anyway after completion, as CompletableFuture.allOf does not do that for you.

Java Generic array creation allowed?

The code is allowed because new jj[10] is not a generic array (e.g. new T[10]), but is instead an array of raw jj instances - jj with its generic arguments omitted. new jj[10] creates a new array of raw jj, not a new array of jj<Integer>. Java allows one to force instances of a raw type to be assigned to a reference of type with generic arguments as the code does with jj<Integer>[] lol = new jj[10];. I believe this is there for backwards compatibility with pre-Java 5 code.

Why can't I create generic array in Java?

You can't create a generic array in Java. Arrays are reifiable types, and carry their type information at runtime whereas generics are non reifiable, and their type information is erased after the compile time due to erasure. This is due to the implementation of the type system in java and even though this causes some rough edges and corner cases, it eases the evolution of code into generics. Since generic type information is erased, you don't have them at runtime and that's why you can't create a generic array in java.

There are two solutions to circumvent the issue you are facing. You can either create an Object array and cast it to the generic type you need.

final T[] arr = (T[]) new Object[n]

or else

final Object[] arr = new Object[]

You can do the cast when you get the items from this array like this

T itm = (T) arr[1];

Both of the above approaches work if you don't return this internal array.

However, if you are returning the internal array, you need to create it reflectively, since you need to maintain the proper reifiable type.

static <T> T[] createArr(Class<T> clz) {
return (T[]) Array.newInstance(clz, 5);
}

Instantiating generic array in Kotlin

JVM arrays, on which Kotlin arrays are mapped to, require the element type to be known at compile time to create an instance of array.

So you can instantiate Array<String> or Array<Any>, but not Array<T> where T is a type parameter, representing the type that is erased at compile time and hence is unknown.
To specify that a type parameter must be known at compile time it is marked with reified modifier.

There are several options, what you can do in this situation:

  1. Use MutableList<T> for storing elements, which doesn't require reified T:

    // MutableList function, available in Kotlin 1.1
    val data = MutableList(rows * cols, { i ->
    val r = i / cols
    init(r, i % cols)
    })
    // or in Kotlin 1.0
    val data = mutableListOf<T>().apply {
    repeat(rows * cols) { i ->
    val r = i / cols
    add(init(r, i % cols))
    }
    }
  2. Create an array from an inline function with reified type parameter:

    inline fun <reified T> Matrix2d(val rows: Int, val cols: Int, init: (Int, Int) -> T) = 
    Matrix2d(rows, cols, Array(rows * cols, { .... })

    class Matrix2d<T>
    @PublishedApi internal constructor(
    val rows: Int, val cols: Int,
    private val data: Array<T>
    )
  3. Use Array<Any?> as the storage, and cast its values to T in get function:

    val data = Array<Any?>(rows * cols, { .... })

    operator fun get(row: Int, col: Int): T = data[row * cols + col] as T
  4. Pass a parameter of type Class<T> or KClass<T> to constructor and use java reflection to create an instance of array.

Generic array cast exception

Works as designed: at runtime there is no generic type.

It gets erased, and an array of Object is created. An array of Object can not be cast to another kind of array.

This is one of the restrictions of Java generics, based on the way how they are implemented.

That is way you are advised to be careful using arrays together with generics. You might prefer to use a generic List instead.

And just to be clear about this: yes, you can get arrays to work with generics, as elegantly shown by the other answers. But: you spend your time fighting symptoms doing so. Arrays and generics don't go together nicely in Java. Accept that, use generic lists and thus: fix the problem instead of working around it.



Related Topics



Leave a reply



Submit