使用 [] 调用时出现奇怪 java.lang.ClassCastException
Strange java.lang.ClassCastException when using [] call
我正在为我的游戏使用 LibDGX 库。我遇到了常见的异常ClassCastException
,但它发生在奇怪的情况下。
我正在使用 LibGDX 库中的 Animation class。
仅当我使用 []
操作时,我才在这一行出现错误 。
val tex = animation.keyFrames[0]
如果我将其更改为 get()
错误消失。
tex = animation.keyFrames.get(0)
这里是完整的方法。我简化了示例。
override fun create() {
val frames = Array<TextureRegion>()
frames.add(TextureRegion(Texture("badlogic.jpg")))
val animation = Animation(frames)
val tex1 = animation.keyFrames.get(0) // Compiles
val tex = animation.keyFrames[0] // error - [Ljava.lang.Object; cannot be cast to [Lcom.badlogic.gdx.graphics.g2d.TextureRegion;
}
奇怪,不是吗?
这是我用来尽量减少其他错误原因的动画 class。它与 LibGDX api.
中的相同
public class Animation<T> {
T[] keyFrames;
private float frameDuration;
public Animation (float frameDuration, Array<? extends T> keyFrames) {
this.frameDuration = frameDuration;
Class arrayType = keyFrames.items.getClass().getComponentType();
T[] frames = (T[]) ArrayReflection.newInstance(arrayType, keyFrames.size);
for (int i = 0, n = keyFrames.size; i < n; i++) {
frames[i] = keyFrames.get(i);
}
setKeyFrames(frames);
}
protected void setKeyFrames (T... keyFrames) {
this.keyFrames = keyFrames;
}
}
我还注意到错误 消失了 如果我使用 Array.with
构造而不是创建 Array<TextureRegion>
我的意思是 LibGDX Array不是来自 Kotlin。
val animation = Animation(Array.with(TextureRegion(Texture("badlogic.jpg"))))
你能告诉我发生这种情况的原因还是可能是错误?
编辑:在旧版本的libGDX中,Animation
不使用Array
的反射能力。如果它有,你就不会出现这个错误,因为数组的类型是正确的!它应该在 1.9.7 版本的 libGDX 中。注意:创建时必须指定Array
的Class
。
最近在 #4476 中修复了这个问题。
但是,下面的 "bug" 与 get
和 []
导致数组的不同字节码可能仍然值得 reporting/inquiring 左右。
这很有趣!
正在反编译
val tex1 = animation.keyFrames.get(0)
给予
ALOAD 2
INVOKEVIRTUAL com/badlogic/gdx/graphics/g2d/Animation.getKeyFrames ()[Ljava/lang/Object;
ICONST_0
AALOAD
CHECKCAST com/badlogic/gdx/graphics/g2d/TextureRegion
ASTORE 3
正在反编译
val tex = animation.keyFrames[0]
给予
ALOAD 2
INVOKEVIRTUAL com/badlogic/gdx/graphics/g2d/Animation.getKeyFrames ()[Ljava/lang/Object;
CHECKCAST [Lcom/badlogic/gdx/graphics/g2d/TextureRegion;
ICONST_0
AALOAD
ASTORE 4
重要的是要注意 keyFrames
的类型是 T[]
,因此它不可能有自定义 get
方法(假设不存在扩展。)
看起来虽然the two should be equal([]
调用了operator fun get
),但它们不是!
我能看到的唯一区别是 get
变体在通过 AALOAD
检索后检查类型 (CHECKCAST
),而 []
变体尝试检索数组后立即检查数组的类型T[]
。
但是,因为frames
是在Animation
的构造函数中声明的:
public Animation (float frameDuration, Array<? extends T> keyFrames) {
this.frameDuration = frameDuration;
T[] frames = (T[]) new Object[keyFrames.size];
for (int i = 0, n = keyFrames.size; i < n; i++) {
frames[i] = keyFrames.get(i);
}
setKeyFrames(frames);
}
作为
T[] frames = (T[]) new Object[keyFrames.size];
数组的实际类型是Object[]
!所以这个转换为 TextureRegion[]
失败了。
对我来说,这似乎是一个 libGDX 错误。
我可以很容易地在 Java:
中用一个简单的 class 重现这个
public class JavaObjWithArray<T> {
private T[] items;
public JavaObjWithArray(T[] items) {
this.items = (T[]) new Object[items.length];
System.arraycopy(items, 0, this.items, 0, items.length);
}
public T[] getItems() {
return items;
}
}
fun main(args: Array<String>) {
val obj = JavaObjWithArray(arrayOf("a", "b"))
val one = obj.items.get(0)
val two = obj.items[0]
}
这也会在 []
的行上抛出一个 ClassCastException
!此外,字节码也有相同的区别。
真正出现问题的原因是 Java(和 Kotlin)中缺少泛型数组。 Java中有两个主要的解决方案:
- 存储
Object[]
并将值转换为 get
- 存储一个
Object[]
,但将其转换为 T[]
这是个问题——因为数组的实际类型是Object[]
。因此,这些 "generic" 数组 不应公开! 我可以理解为什么 libGDX 这样做,即提高性能并删除方法的小开销电话,但这绝对不安全。
get
和 []
不同的事实 可能 是一个错误。也许对此进行调查或提交错误报告可能会有所帮助。
我正在为我的游戏使用 LibDGX 库。我遇到了常见的异常ClassCastException
,但它发生在奇怪的情况下。
我正在使用 LibGDX 库中的 Animation class。
仅当我使用 []
操作时,我才在这一行出现错误 。
val tex = animation.keyFrames[0]
如果我将其更改为 get()
错误消失。
tex = animation.keyFrames.get(0)
这里是完整的方法。我简化了示例。
override fun create() {
val frames = Array<TextureRegion>()
frames.add(TextureRegion(Texture("badlogic.jpg")))
val animation = Animation(frames)
val tex1 = animation.keyFrames.get(0) // Compiles
val tex = animation.keyFrames[0] // error - [Ljava.lang.Object; cannot be cast to [Lcom.badlogic.gdx.graphics.g2d.TextureRegion;
}
奇怪,不是吗?
这是我用来尽量减少其他错误原因的动画 class。它与 LibGDX api.
中的相同 public class Animation<T> {
T[] keyFrames;
private float frameDuration;
public Animation (float frameDuration, Array<? extends T> keyFrames) {
this.frameDuration = frameDuration;
Class arrayType = keyFrames.items.getClass().getComponentType();
T[] frames = (T[]) ArrayReflection.newInstance(arrayType, keyFrames.size);
for (int i = 0, n = keyFrames.size; i < n; i++) {
frames[i] = keyFrames.get(i);
}
setKeyFrames(frames);
}
protected void setKeyFrames (T... keyFrames) {
this.keyFrames = keyFrames;
}
}
我还注意到错误 消失了 如果我使用 Array.with
构造而不是创建 Array<TextureRegion>
我的意思是 LibGDX Array不是来自 Kotlin。
val animation = Animation(Array.with(TextureRegion(Texture("badlogic.jpg"))))
你能告诉我发生这种情况的原因还是可能是错误?
编辑:在旧版本的libGDX中,Animation
不使用Array
的反射能力。如果它有,你就不会出现这个错误,因为数组的类型是正确的!它应该在 1.9.7 版本的 libGDX 中。注意:创建时必须指定Array
的Class
。
最近在 #4476 中修复了这个问题。
但是,下面的 "bug" 与 get
和 []
导致数组的不同字节码可能仍然值得 reporting/inquiring 左右。
这很有趣!
正在反编译
val tex1 = animation.keyFrames.get(0)
给予
ALOAD 2 INVOKEVIRTUAL com/badlogic/gdx/graphics/g2d/Animation.getKeyFrames ()[Ljava/lang/Object; ICONST_0 AALOAD CHECKCAST com/badlogic/gdx/graphics/g2d/TextureRegion ASTORE 3
正在反编译
val tex = animation.keyFrames[0]
给予
ALOAD 2 INVOKEVIRTUAL com/badlogic/gdx/graphics/g2d/Animation.getKeyFrames ()[Ljava/lang/Object; CHECKCAST [Lcom/badlogic/gdx/graphics/g2d/TextureRegion; ICONST_0 AALOAD ASTORE 4
重要的是要注意 keyFrames
的类型是 T[]
,因此它不可能有自定义 get
方法(假设不存在扩展。)
看起来虽然the two should be equal([]
调用了operator fun get
),但它们不是!
我能看到的唯一区别是 get
变体在通过 AALOAD
检索后检查类型 (CHECKCAST
),而 []
变体尝试检索数组后立即检查数组的类型T[]
。
但是,因为frames
是在Animation
的构造函数中声明的:
public Animation (float frameDuration, Array<? extends T> keyFrames) {
this.frameDuration = frameDuration;
T[] frames = (T[]) new Object[keyFrames.size];
for (int i = 0, n = keyFrames.size; i < n; i++) {
frames[i] = keyFrames.get(i);
}
setKeyFrames(frames);
}
作为
T[] frames = (T[]) new Object[keyFrames.size];
数组的实际类型是Object[]
!所以这个转换为 TextureRegion[]
失败了。
对我来说,这似乎是一个 libGDX 错误。
我可以很容易地在 Java:
中用一个简单的 class 重现这个public class JavaObjWithArray<T> {
private T[] items;
public JavaObjWithArray(T[] items) {
this.items = (T[]) new Object[items.length];
System.arraycopy(items, 0, this.items, 0, items.length);
}
public T[] getItems() {
return items;
}
}
fun main(args: Array<String>) {
val obj = JavaObjWithArray(arrayOf("a", "b"))
val one = obj.items.get(0)
val two = obj.items[0]
}
这也会在 []
的行上抛出一个 ClassCastException
!此外,字节码也有相同的区别。
真正出现问题的原因是 Java(和 Kotlin)中缺少泛型数组。 Java中有两个主要的解决方案:
- 存储
Object[]
并将值转换为get
- 存储一个
Object[]
,但将其转换为T[]
这是个问题——因为数组的实际类型是Object[]
。因此,这些 "generic" 数组 不应公开! 我可以理解为什么 libGDX 这样做,即提高性能并删除方法的小开销电话,但这绝对不安全。
get
和 []
不同的事实 可能 是一个错误。也许对此进行调查或提交错误报告可能会有所帮助。