问题
I am extending Array
from LibGDX in a generic fashion which extends Array<MyClass>
but when I access its items
array directly, I get an error that Object
can not be cast to class MyClass
.
I don't see exactly why I'm getting an error with this.
public class MyArray extends Array<MyClass> {
public void method() {
for (MyClass myItem : items)//throws java.lang.Object; cannot be cast to class error on this line
System.out.println(myItem);
}
}
I noticed also that if I try to do a similar thing with items[2]
throws the same error while get(2)
has no problem...
EDIT: Here is the Array
I used from LibGDX Array
回答1:
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
}
}
}
来源:https://stackoverflow.com/questions/63323421/libgdx-arrays-internal-array-is-not-type-safe