LibGDX 数组的内部数组类型不安全
LibGDX Array's internal array is not type safe
我正在以一种通用的方式从 LibGDX 扩展 Array
extends Array<MyClass>
但是当我直接访问它的 items
数组时,我得到一个错误 Object
无法转换为 class MyClass
.
我不明白为什么会出现此错误。
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);
}
}
我还注意到,如果我尝试用 items[2]
做类似的事情会抛出同样的错误,而 get(2)
没有问题...
编辑:这是我从 LibGDX Array
使用的 Array
说明
其根本原因是,在 Java 中,泛型在 运行 时间 被 删除。所以 LibGDX 的 T[] items
在编译后只是一个普通的 Object[]
.
因此,虽然您的代码确实可以编译,因为您使用了正确的类型,但是当您 运行 它时,Java 检测到一个可能的问题,因为您试图将 Object
视为MyClass
。因为,在 运行 时,数组只是一个 Object[]
,它的所有内容都是 Object
。所以泛型在 运行 时间内无法保持活动状态。
反思
真正拥有 true T[]
的唯一方法是通过使用实际真实类型的反射动态创建它,作为标记给出。 LibGDX 为此提供了一个构造函数:
public Array (boolean ordered, int capacity, Class arrayType) {
this.ordered = ordered;
items = (T[]) ArrayReflection.newInstance(arrayType, capacity);
}
这样,数组在 运行 时也将是 T[]
类型。它实际上也在 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
get
调用有效,因为在这种情况下 Java 足够聪明,可以确定擦除实际上是安全的。因此,虽然 MyClass foo = get(index);
确实会衰减到 MyClass foo = (MyClass) get(index);
,但 Java 知道这是一个 safe cast.
在您直接使用数组的示例中,Java 无法解决并失败。对于确切的细节,您可能需要深入研究 JLS。
items.length
在您的 child class 中以任何方式使用 items
都会立即触发问题,所以即使是这个看似无辜的片段:
int size = items.length;
这是一个非常技术性的边缘案例,也是 Java 通用系统的局限性。您的 child class 在使用 items
时期望 MyClass[]
但它在 运行 时得到 Object[]
,这不是它想要的。所以它触发了错误。
另一个例子
这是另一个在不使用 LibGDX 的情况下重现问题的最小示例。您可以轻松重现所见情况。
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
}
}
}
我正在以一种通用的方式从 LibGDX 扩展 Array
extends Array<MyClass>
但是当我直接访问它的 items
数组时,我得到一个错误 Object
无法转换为 class MyClass
.
我不明白为什么会出现此错误。
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);
}
}
我还注意到,如果我尝试用 items[2]
做类似的事情会抛出同样的错误,而 get(2)
没有问题...
编辑:这是我从 LibGDX Array
使用的Array
说明
其根本原因是,在 Java 中,泛型在 运行 时间 被 删除。所以 LibGDX 的 T[] items
在编译后只是一个普通的 Object[]
.
因此,虽然您的代码确实可以编译,因为您使用了正确的类型,但是当您 运行 它时,Java 检测到一个可能的问题,因为您试图将 Object
视为MyClass
。因为,在 运行 时,数组只是一个 Object[]
,它的所有内容都是 Object
。所以泛型在 运行 时间内无法保持活动状态。
反思
真正拥有 true T[]
的唯一方法是通过使用实际真实类型的反射动态创建它,作为标记给出。 LibGDX 为此提供了一个构造函数:
public Array (boolean ordered, int capacity, Class arrayType) {
this.ordered = ordered;
items = (T[]) ArrayReflection.newInstance(arrayType, capacity);
}
这样,数组在 运行 时也将是 T[]
类型。它实际上也在 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
get
调用有效,因为在这种情况下 Java 足够聪明,可以确定擦除实际上是安全的。因此,虽然 MyClass foo = get(index);
确实会衰减到 MyClass foo = (MyClass) get(index);
,但 Java 知道这是一个 safe cast.
在您直接使用数组的示例中,Java 无法解决并失败。对于确切的细节,您可能需要深入研究 JLS。
items.length
在您的 child class 中以任何方式使用 items
都会立即触发问题,所以即使是这个看似无辜的片段:
int size = items.length;
这是一个非常技术性的边缘案例,也是 Java 通用系统的局限性。您的 child class 在使用 items
时期望 MyClass[]
但它在 运行 时得到 Object[]
,这不是它想要的。所以它触发了错误。
另一个例子
这是另一个在不使用 LibGDX 的情况下重现问题的最小示例。您可以轻松重现所见情况。
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
}
}
}