使用[]调用时出现奇怪的java.lang.ClassCastException

ica*_*bas 3 java reflection libgdx kotlin

我正在为我的游戏使用LibDGX库.我遇到了常见的异常ClassCastException,但它发生在奇怪的情况下.

我正在使用LibGDX库中的Animation类.

只有在我使用操作时,我才会在此行上出错.[]

val tex = animation.keyFrames[0]
Run Code Online (Sandbox Code Playgroud)

如果我将其更改为get() 错误消失.

tex = animation.keyFrames.get(0)
Run Code Online (Sandbox Code Playgroud)

这是完整的方法.我简化了例子.

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;


}
Run Code Online (Sandbox Code Playgroud)

奇怪,不是吗?

这是我用来最小化其他错误原因的Animation类.它与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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我还注意到,如果我使用构造而不是创建我的意思是LibGDX 数组而不是来自Kotlin ,则错误消失.Array.withArray<TextureRegion>

val animation = Animation(Array.with(TextureRegion(Texture("badlogic.jpg"))))
Run Code Online (Sandbox Code Playgroud)

你能告诉我出现这种情况的原因还是可能是一个错误?

Moi*_*ira 5

编辑:在旧版本的libGDX中,Animation不使用Array反射功能.如果,你就不会有这个错误,因为数组的类型是正确的!它应该在版本1.9.7中的libGDX中.注意:您必须指定ClassArray创建时.

最近在#4476修复了这个问题.但是,以下"错误" get[]导致阵列的不同字节码仍然值得报告/查询.


这是有趣的!

反编译

val tex1 = animation.keyFrames.get(0)
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

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

需要注意的是的类型是很重要keyFramesT[],所以它不可能有一个自定义的get方法(假设没有扩展存在.)

似乎虽然两者应该相等([]调用operator fun get),但它们不是!

我可以看到的唯一区别是get变量CHECKCAST在检索后通过检查type()AALOAD,而[]变体尝试在检索数组后T[] 立即检查数组的类型.

但是,因为framesAnimation构造函数中声明:

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);
}
Run Code Online (Sandbox Code Playgroud)

T[] frames = (T[]) new Object[keyFrames.size];
Run Code Online (Sandbox Code Playgroud)

数组的实际类型是Object[]!所以这个演员TextureRegion[]失败了.

对我来说,这似乎是一个libGDX错误.


我可以使用Java中的一个简单类来轻松地重现这个:

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]
}
Run Code Online (Sandbox Code Playgroud)

这也将抛出一条ClassCastException线[]!此外,字节码具有相同的差异.


问题确实出现在Java(和Kotlin)中缺少通用数组.Java中有两种主要的解决方案:

  • 存储Object[]并投射值get
  • 存储Object[],但将其投射到T[]

这是一个问题 - 因为数组的实际类型 Object[].因此,不应暴露这些"通用"阵列!我可以理解为什么libGDX会这样做,即提高性能并消除方法调用的小开销,但这绝对不安全.


这样的事实get[]不同,可能是一个错误,但.也许查看这个或提交错误报告可能会有所帮助.


Java字节码指令列表