java - LibGDX Array's internal array is not type safe
问题描述
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
解决方案
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
}
}
}
推荐阅读
- powershell - 存储多维数据以提高速度的最佳类型
- node.js - 时刻时区无法在 AWS Lambda 中加载数据
- java - 将有序链表转换为平衡 BST
- hyperledger-fabric - E2E_CLI 运行 Fabric 示例 e2e_cli script.sh 时出错(错误:无法读取块:&{SERVICE_UNAVAILABLE}
- azure - 无法通过资源组“自动化脚本”下载现有功能代码 JSON 文件以在 ARM 部署中使用
- python - 覆盖 classInstance.attribute[index]
- ajax - Django 测试 - 测试处理 ajax 调用的视图
- c# - 如何在 ClosedXML 的合并单元格范围内居中图像
- java - 使用 BootsFaces 页面直接错误构建的 Java JSF 网站
- spring - 如何在 Spring 批处理中读取 MS Access db(.mdb 文件)并加载到 mysql db