Extraña java.lang.ClassCastException al usar la llamada
Estoy usando la biblioteca LibDGX para mi juego. Y me enfrenté a la exception común ClassCastException
, pero ocurre en casos extraños.
Estoy usando la class de Animación de la biblioteca LibGDX.
- Kotlin: ¿"eso" asignable?
- Reglas de ProGuard para la reflexión de Kotlin
- reflexión kotlin get list de campos
- Cómo crear instancia de class genérica pasando una reference de método en constructor con kotlin
- ¿Cuál es una forma adecuada de crear una instancia de class a partir de KType?
Estoy recibiendo un error en esta línea solo si estoy usando la operación []
.
val tex = animation.keyFrames[0]
Si lo cambio a get()
error desaparece .
tex = animation.keyFrames.get(0)
Aquí está el método completo. Yo simplifiqué el ejemplo.
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; }
Extraño, ¿verdad?
Aquí está la class de animation que utilicé para minimizar otros motivos de error. Es lo mismo que en 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; } }
También noté que el error desaparece si utilizo Array.with
construction en lugar de crear Array<TextureRegion>
Me refiero a LibGDX Array no de Kotlin.
val animation = Animation(Array.with(TextureRegion(Texture("badlogic.jpg"))))
¿Podría decirme por qué ocurre esto o es posible que haya un error?
Editar : en versiones anteriores de libGDX, Animation
no usa las capacidades de reflexión de Array
. Si lo hubiera hecho , no tendrías este error, ¡porque la matriz sería del tipo correcto! Debería estar en libGDX en la versión 1.9.7. Nota: debe especificar la Class
de la Array
al crearla.
Esto fue arreglado recientemente en # 4476 . Sin embargo, el siguiente "error" con get
y []
da como resultado un bytecode diferente para una matriz aún puede valer la pena informar / preguntar acerca de.
¡Esto es interesante!
Descomstackndo
val tex1 = animation.keyFrames.get(0)
da
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
mientras descomstack
val tex = animation.keyFrames[0]
da
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
Es importante tener en count que el tipo de keyFrames
es T[]
, por lo que no es posible que tenga un método get
personalizado (suponiendo que no existan extensiones).
Parece que aunque los dos deberían ser iguales ( []
invoca la operator fun get
), ¡no lo son!
La única diferencia que puedo ver es que la variante get
comtesting el tipo ( CHECKCAST
) después de recuperarlo a través de AALOAD
, mientras que la variante []
intenta comprobar que el tipo de la matriz es T[]
inmediatamente después de recuperar la matriz.
Sin embargo, debido a que los frames
se declaran en el constructor de 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); }
como
T[] frames = (T[]) new Object[keyFrames.size];
¡el tipo real de la matriz es Object[]
! Así que este lanzamiento a TextureRegion[]
falla.
Para mí, esto parece un error libGDX.
Puedo reproducir esto muy fácilmente con una class simple en 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] }
Esto también arrojará una ClassCastException
en la línea con []
! Además, el bytecode tiene la misma diferencia.
El problema realmente surge con la falta de matrices genéricas en Java (y Kotlin). Hay dos soluciones principales para esto en Java:
- Almacene un
Object[]
y eche los valores alget
- Almacenar un
Object[]
, pero lanzarlo aT[]
Esto es un problema, porque el tipo real de la matriz es Object[]
. ¡Como consecuencia, estas matrices "genéricas" no deberían exponerse! Puedo entender por qué libGDX lo hace, es decir, para mejorar el performance y eliminar la pequeña sobrecarga de llamadas a methods, pero esto definitivamente no es seguro.
Sin embargo, el hecho de que get
y []
difieran puede ser un error. Tal vez investigar esto o presentar un informe de error puede ayudar.
Lista de instrucciones de bytecode de Java