Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can we use array with generic reference

While answering to a question about that here: https://stackoverflow.com/a/9872630/82609

I tried to do the following:

Comparator<String>[] comparators = new Comparator[] {...};

It works! But the following doesn't:

Comparator<String>[] comparators = new Comparator<String>[] {...};

On the related question, i made the assumption:

I guess it's because initially the array contract may be something like this:

If you create an array of type X, you will NEVER EVER be able to put anything in it that IS-NOT-AN X. If you try, you'll get an ArrayStoreException

Thus allowing arrays with generics creation would lead to a different rule like:

If you create an array of type X<Y>, you will NEVER EVER be able to put anything that IS-NOT-AN X. If you try, you'll get an ArrayStoreException. But you CAN add both X<Y> and X<Z> objects because of type erasure!


But thinking about it, would it really be a problem to have:

Comparator<String>[] comparators = new Comparator<String>[] {...};

I don't really understand why it's not possible, since using such a thing would:

  • Check the classes inserted at runtime
  • Check the classes type inserted at compile time

Finally we can use an array with generic type reference and because of the impossibility to create an array with a generic type, i think many people do not even know it's possible.

I just wonder if someone knows the reason behind this choice?

It's a bit like forcing people to use List<String> = new ArrayList(); instead of using List<String> = new ArrayList<String>();


dimitrisli you gave a nice exemple from Joshua Bloch's famous book. As you/he explained it, it is dangerous to use both generic arrays + covariance and could lead to ClassCastException while we expect ArrayStoreException from an array with use of covariance.

But please notice the following is still legal and lead to the same:

List<String>[] stringLists = new List[1];
List<Integer> intList = Arrays.asList(42);
Object[] objects = stringLists;
objects[0] = intList;
String s = stringLists[0].get(0);

However it produces an unchecked cast warning at compile time, and as you mentionned, a ClassCastException at runtime.

like image 920
Sebastien Lorber Avatar asked Mar 27 '12 09:03

Sebastien Lorber


People also ask

Can we use generic with array?

Java allows generic classes, methods, etc. that can be declared independent of types. However, Java does not allow the array to be generic. The reason for this is that in Java, arrays contain information related to their components and this information is used to allocate memory at runtime.

Is generic array creation allowed in Java?

You can't have arrays of generic classes. Java simply doesn't support it. and then create an array of MyObjectArrayList .

Is array generic in C#?

In C# 2.0 and later, single-dimensional arrays that have a lower bound of zero automatically implement IList<T>. This enables you to create generic methods that can use the same code to iterate through arrays and other collection types. This technique is primarily useful for reading data in collections.


1 Answers

I see where you're coming from (and in a practical sense I basically agree), but I think there is a difference that motivates the current situation.

As you mention, erasure means that generic parameters aren't available at runtime and so the types are checked at compile time (be that for a List<String> or your Comparator<String>[]). Critically, this is based on the generic parameter of the variable.

Arrays on the other hand check the types of their argument at runtime, when they're inserted, so they can throw an ArrayStoreException if they're misused (typically due to abusing their covariance). Arrays therefore need to be able to perform both of your bullet point checks internally, and of course they can't check the generic parameter at runtime. Hence it makes no sense to instantiate a generic array, since the array would have to completely ignore the generic parameter, which would be misleading at best.

That said, it does make sense to assign such an array to a parameterised reference, since then the compiler can perform generic checks. And you're right in thinking that this covers all the bases and ensures that the generic types are checked (so long as the variables are parameterised correctly).

The bottom-line reason behind this choice, and why arrays are different to collections in this respect, is that arrays are required to actually check the types of their arguments when they're inserted, whereas collections merely take your word for it and allow type errors to bubble through into a ClassCastException later.

like image 178
Andrzej Doyle Avatar answered Sep 24 '22 09:09

Andrzej Doyle