What is a Scalar type and why is it more dangerous to suppress uncontrolled casting to an array type than to a scalar type?

From an Effective Java Element 26 Use Generic Types

All other things being equal, it is more dangerous to suppress uncontrolled casting to an array type than to a scalar type, which is the second solution. But in a more realistic generic class than Stack, you will probably be read from the array at many points in the code, so choosing a second solution will require many castings for E than a single cast to E [], so the first solution is used more commonly [Naftalin07, 6.7 ].

What does the author mean here scalar type and what is he trying to convey here? What is option 1 considered more dangerous than option 2?

Code:

 // The elements array will contain only E instances from push(E). // This is sufficient to ensure type safety, but the runtime // type of the array won't be E[]; it will always be Object[]! @SuppressWarnings("unchecked") public Stack() { elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; } 

VS

 // Appropriate suppression of unchecked warning public E pop() { if (size == 0) throw new EmptyStackException(); // push requires elements to be of type E, so cast is correct @SuppressWarnings("unchecked") E result = (E) elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } 
+4
source share
3 answers

Ideally, we want to write

 E[] elements; public Stack() { elements = new E[DEFAULT_INITIAL_CAPACITY]; } 

Unfortunately, Java made a huge mistake and did not allow this. Therefore, we need workarounds.

This is a workaround.

 E[] elements; public Stack() { elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; } 

theoretically incorrect in a system like Java, because Object[] not a subtype of E[] . It happens that it works while working on modern JVMs, but we should not rely on the fact that we work forever. Well, actually, people are counting on it, and I don’t see any chance in hell that this will change. Therefore no one cares.

(correction: in fact, the §5.5 language specification specifically allows the application to run at run time, so the code is not erroneous for each specification. However, it is too hacky, it is not part of the "normal" type system, its correctness is based on some compromises that we don’t really want to study.

This second solution is correct, both practically and theoretically.

 Object[] elements; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public push(E e) { ... elements[size++] = e; } public E pop() { ... E result = (E)element[size--]; } 

casting from an object to E is correct, because the program logic ensures that it must be E

+4
source

The scalar type in this example is a single value, not an array, which consists of several values, such as a mathematical vector. E [] is an array, and E is a scalar.

My initial thought was that Joshua Bloch believes that it is more dangerous to suppress an uncontrolled throw warning in the case of arrays, because it is harder to prove that nothing will happen to the security type of your code.

Another opinion worth considering was mentioned by Ruach in the comments: “I would have thought it was not so much the complexity of the proof as it was about finding an error when there is an error. I think that in general there will be less“ distance ”between the erroneous but uncontrolled an act (E) followed by an implicit cast that throws a ClassCastException than if instead of cast (E []) instead of <

And the third opinion (if I understand correctly, this is what irrefutably wants to indicate in his answer, and in any case this is my new opinion) is that casting an array is "risky" because this array cannot be used outside of this class. (E[]) is an unverified actor: due to type erasure, the runtime cannot verify the correctness of this (incorrect) translation. We clean up with a dirty trick, but if some method returned this array as E [] and it would be assigned to E [] in the client class, it still fails at runtime with a ClassCastException:

 public class Test { public static void main(String[] args) { Stack<String> stack = new Stack<String>(); String[] array = stack.getArray(); // ClassCastException at runtime here! } } class Stack<E> { E[] elements; public Stack() { elements = (E[]) new Object[10]; } // oh no, our dirty-tricky array escapes! E[] getArray() { return elements; } } 
+4
source

A scalar type in this case means a type without an array.

+1
source

All Articles