How to create a shared array?

I do not understand the relationship between generics and arrays.

I can create a reference to an array with a common type:

private E[] elements; //GOOD 

But you cannot create an array object with a common type:

 elements = new E[10]; //ERROR 

But it works:

 elements = (E[]) new Object[10]; //GOOD 
+77
java generics
Sep 02 '13 at 21:30
source share
4 answers

You cannot mix arrays and generics. They do not go well together. There are differences in how arrays and generic types provide type checking. We say that arrays are characterized, but there are no generics. As a result of this, you see that these differences work with arrays and generics.

Arrays are covariant, there are no generics:

What does it mean? You should already know that the following assignment is valid:

 Object[] arr = new String[10]; 

Basically, Object[] is a supertype of String[] , because Object is a supertype of String . This does not apply to generics. Therefore, the following declaration is invalid and will not compile:

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

Recognition of the cause, generics are invariant.

Checking the type of check:

Generalizations were introduced in Java to provide stronger type checking at compile time. Thus, generic types do not have any type information at runtime due to the type of erasure . Thus, List<String> has a static type List<String> , but a dynamic type List .

However, arrays carry information such as runtime type of component. At run time, arrays use the Array repository check to check if you are inserting elements compatible with the actual type of the array. So the following code:

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

will compile fine, but will work at runtime, resulting in an ArrayStoreCheck. With generics, this is not possible, since the compiler will try to prevent an exception at runtime by providing compile-time checks, avoiding creating a link like this, as shown above.

So what's the problem with Generic Array Creation?

Creating an array whose component type is either a type parameter, or a specific parameterized type, or a restricted wildcard parameter, type-unsafe .

Consider the code as shown 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 T not known at run time, the created array is actually Object[] . Therefore, the above method at runtime will look like this:

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

Now suppose you call this method the following:

 Integer[] arr = getArray(10); 

Here is the problem. You just assigned Object[] Integer[] link. The above code will compile fine, but will work at runtime.

Therefore, creating a shared array is prohibited.

Why does casting new Object[10] to E[] ?

Now your last doubt why the below code works:

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

The above code has the same consequences as explained above. If you notice, the compiler will give you a warning about immediate growth, since you are attributing an array of an unknown type of component. This means that execution may fail. For example, if you have this code in the above method:

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

and you call it like this:

 String[] arr = getArray(10); 

this will crash at runtime with a ClassCastException. Thus, this method will not always work.

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

The question is the same. Due to type erasure, List<String>[] is nothing more than List[] . So, if you allowed the creation of such arrays, 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 this should have thrown an ArrayStoreException. This is because both List<String>[] and List<Integer>[] compiled to List[] at runtime.

So, is it possible to create an array of unlimited wildcard parameters with parameters?

Yes. The reason is that a List<?> Is a reproducible type. And that makes sense, as there is no type related at all. Thus, as a result of type erasure, there is nothing to lose. Thus, it is completely safe to create an array of this type.

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

Both of the above cases are accurate because List<?> Is the supertype of the entire instance of the generic type List<E> . Thus, at runtime, it will not throw an ArrayStoreException. The case is similar to an array of raw types. Since raw types can also be re-identified, you can create an array of List[] .

So, it looks like you can only create an array of duplicate types, but not non-recoverable types. Please note that in all the above cases, the array declaration is fine, it is creating an array with the new operator, which gives problems. But it makes no sense to declare an array of these reference types, since they cannot point to anything other than null (Ignoring unlimited types).

Is there any workaround for E[] ?

Yes, you can create an array using the 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 this method returns Object . But you can be sure that this is a safe throw. So you can use @SuppressWarnings for this variable.

+172
Sep 02 '13 at 21:59 on
source share

The problem is that during erasing the general type is erased , so new E[10] will be equivalent to new Object[10] .

This would be dangerous because one could add data other than type E to the array. This is why you need to explicitly specify the type you want with

+3
02 Sep
source share

Here is the implementation of LinkedList<T>#toArray(T[]) :

 public <T> T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; if (a.length > size) a[size] = null; return a; } 

In short, you can only create shared arrays through Array.newInstance(Class, int) , where int is the size of the array.

+2
02 Sep '13 at 21:35
source share

checked:

 public Constructor(Class<E> c, int length) { elements = (E[]) Array.newInstance(c, length); } 

or unverified:

 public Constructor(int s) { elements = new Object[s]; } 
+1
Sep 02 '13 at 9:49 on
source share



All Articles