Why can we use a shared reference array

Responding to a question about this here: https://stackoverflow.com/a/312969/

I tried to do the following:

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

It works! But the following:

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

On the relevant question, I made the assumption:

I assume that initially the contract with the array could be something like this:

If you create an array of type X, you NEVER NEVER put something in it that IS-NOT-AN X. If you try, you will get an ArrayStoreException

Thus, creating arrays with creating generics will result in a rule like:

If you create an array of type X<Y> , you NEVER can ever put anything that is NOT-NOT-A X. If you try, you will get an ArrayStoreException. But you can add objects X<Y> and X<Z> due to erasing styles!


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

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

I really don't understand why this is not possible, since using such a thing:

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

Finally, we can use an array with a typical type reference and because of the impossibility of creating an array with a common type, I think many people do not even know that this is possible.

I'm just wondering if anyone knows the reason for this choice?

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


dimitrisli you gave a good example from the famous book of Joshua Bloch. As you have explained this, it is dangerous to use both shared arrays + covariance and cause a ClassCastException, while we expect an ArrayStoreException from the array using covariance.

But please note that it is still legal and leads 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, when compiling, it throws a warning about unverified warnings, and, as you noted, a ClassCastException at runtime.

+6
source share
2 answers

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

As you noted, erasing means that general options are not available at runtime, so types are checked at compile time (be it List<String> or your Comparator<String>[] ). Critically, this is based on a common variable parameter.

Arrays, on the other hand, check the types of their arguments at run time when they are inserted, so they can throw an ArrayStoreException if they are used incorrectly (usually due to abuse of their covariance). Therefore, arrays should be able to perform both checks of your marker point domestically, and, of course, they cannot check the general parameter at runtime. Therefore, there is no point in creating an instance of a common array, since the array should completely ignore the general parameter, which is misleading at best.

Nevertheless, it makes sense to assign such an array to a parameterized reference, since then the compiler can perform general checks. And you are right in thinking that this covers all the bases and ensures that common types are checked (as long as the variables are parameterized correctly).

The underlying reason for this choice and why arrays are different from collections in this regard is that arrays should really check the types of their arguments when they are inserted, whereas collections just take your word and resolve type errors in ClassCastException later.

+4
source

Quote from the great Effective Second Edition of Java p. 120:

Why creating a shared array is illegal - will not compile!

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

Let's pretend that line 1, which creates a shared array, is legal. Line 2 creates and initializes a List<Integer> containing one element. Line 3 stores the List<String> in an object array variable, which is legal because arrays are covariant. Line 4 saves the List<Integer> into a single element An array of objects that succeeds because generic tools are implemented by erasing: The execution type of the List<Integer> instance is just List, and the execution type is a List<String>[] instance of List[] , therefore, this assignment does not ArrayStoreException an ArrayStoreException . Now were in trouble. Weve saved the List<Integer> instance into an array that is declared to hold only List<String> instances. On line 5, we extract the only item from the only list in this array. The compiler automatically discards the extracted element into String, but its Integer, so we get a ClassCastException at runtime. To prevent this from happening, line 1 (which creates a shared array) generates a compile-time error.

+1
source

Source: https://habr.com/ru/post/911731/


All Articles