Explanation
The underlying cause of this is that, in Java, generics are erased at runtime. So the T[] items
of LibGDX, after compilation, is just a plain Object[]
.
So while your code actually does compile because you used the correct type, when you run it, Java detects a possible issue because you are trying to treat Object
s as MyClass
. Since, again, at runtime, the array is just a Object[]
and all its contents are Object
. So the generics cant be kept alive during runtime.
Reflection
The only way to actually have a true T[]
is to create it dynamically via reflection with the actual real type, given as token. LibGDX offers a constructor for that:
public Array (boolean ordered, int capacity, Class arrayType) {
this.ordered = ordered;
items = (T[]) ArrayReflection.newInstance(arrayType, capacity);
}
That way, the array will also at runtime be of type T[]
. It actually also commented this in the source code:
Provides direct access to the underlying array. If the Array's generic type is not Object, this field may only be accessed if the Array#Array(boolean, int, Class)
constructor was used.
get
The get
call works because in this situation Java is smart enough to figure out that the erasure is actually safe. So while MyClass foo = get(index);
indeed decays to MyClass foo = (MyClass) get(index);
, Java knows that this is a safe cast.
In your example where you use the array directly, Java can not figure that out and fails. For the exact details you probably have to dig into the JLS.
items.length
Using items
in any way in your child class will immediatly trigger the problem, so even this innocent looking snippet:
int size = items.length;
This is a very technical edge case and limitation of Javas generic system. Your child class expects a MyClass[]
when using items
but it gets an Object[]
at runtime, which is not what it wants. So it triggers the error.
Another example
Here is another minimal example that reproduces the issue without using any LibGDX. You can easily reproduce the situation as seen.
public class Test {
public static void main(String[] args) {
Child child = new Child();
child.printAll();
System.out.println(child.size());
}
private static class Parent<T> {
protected T[] items;
public Parent() {
items = (T[]) new Object[10];
}
}
private static class Child extends Parent<String> {
public Child() {
items[0] = "hello"; // Fails at runtime
}
public void printAll() {
for (String s : items) { // Fails at runtime
System.out.println(s);
}
}
public int size() {
return items.length; // Fails at runtime
}
}
}