Can anyone explain to me why this happens:
public class Array<E> {
public E[] elements = (E[]) new Object[10];
public E get(int idx) {
return elements[idx]; // Ignore bound-checking for brevity.
}
public static void doSomething(Array<Integer> arr) {
Integer good = arr.get(0);
Integer bad1 = arr.elements[0];
Integer bad2 = ((Integer[]) arr.elements)[0];
Integer bad3 = (Integer) arr.elements[0];
// `bad1', `bad2', and `bad3' all produce a
// runtime exception.
}
public static void main(String[] args) {
Array<Integer> test = new Array<>();
Array.doSomething(test);
}
}
Full example here: http://pastebin.com/et7sGLGW
I've read about type erasure and realize type-checking is performed during compilation and then E is simply replaced with Object, so all we have is public Object[] elements, but why does the get method succeed where regular type-casting doesn't? Doesn't the get's method return type also get erased?
Thanks.
Even though arr has type Array<Integer> (and arr.elements has type Integer[]), arr.elements actually has runtime-type Object[], because the actual array is an instance of type Object[].
(Note that arrays, unlike generics, are covariant, and do not have erasure. Object[] foo = new String[5]; is legal — as is String[] bar = (String[]) foo;. Whereas Integer[] baz = (Integer[]) foo; would raise a ClassCastException at runtime.)
So the reason that any reference to arr.elements triggers a runtime-exception is that the compiler automatically inserts a downcast to Integer[], to bring the type and the runtime-type back into accord. Inside the body of doSomething, arr.elements actually means (Integer[]) arr.elements, with an implicit cast.
By contrast, inside get(), the type of this is just Array<E>, so the type of elements is just E[], which cannot be checked. So the compiler does not insert any implicit casts.
The major take-home point is that (E[]) new Object[10]; is actually incorrect. new Object[10] does not create an instance of E[]. The compiler can't see that it's incorrect, but it will insert lots of casts that will see that it's incorrect.
A better approach is to use Object[] elements = new Object[], and to perform correct-but-unchecked casts from Object to E, when necessary, rather than incorrect-and-unchecked casts from Object[] to E[].
Do you see what I mean?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With