I'm using LibDGX library for my game. And i faced common exception ClassCastException
, but it occurs in strange case.
I'm using Animation class from LibGDX library.
I'm getting error on this line only if i am using []
operation.
val tex = animation.keyFrames[0]
If i change it to get()
error disappears.
tex = animation.keyFrames.get(0)
Here is full method. I simplified example.
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;
}
Strange, isn't it?
Here is Animation class that i used to minimise other error reasons. It is the same as in 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;
}
}
I also noticed that error disappears if i use Array.with
construction instead of creating Array<TextureRegion>
I mean LibGDX Array not from Kotlin.
val animation = Animation(Array.with(TextureRegion(Texture("badlogic.jpg"))))
Could you tell me the reason why this occurs or it is possible a bug?
Edit: In older versions of libGDX,
Animation
does not useArray
's reflection capabilities. If it had, you wouldn't have this error, because the array would be of the correct type! It should be in libGDX in version 1.9.7. Note: you must specify theClass
of theArray
when creating it.This was fixed recently in #4476. However, the following "bug" with
get
and[]
resulting in different bytecode for an array may still be worth reporting/inquiring about.This is interesting!
Decompiling
gives
while decompiling
gives
It is important to note that the type of
keyFrames
isT[]
, so it cannot possibly have a customget
method (assuming no extensions exist.)It seems that although the two should be equal (
[]
invokesoperator fun get
), they are not!The only difference I can see is that the
get
variant checks the type (CHECKCAST
) after retrieving it viaAALOAD
, while the[]
variant attempts to check that the type of the array isT[]
immediately after retrieving the array.However, because
frames
is declared inAnimation
's constructor:as
the actual type of the array is
Object[]
! So this cast toTextureRegion[]
fails.To me, this seems like a libGDX bug.
I am able to very easily reproduce this with a simple class in Java:
This will also throw a
ClassCastException
on the line with[]
! Additionally, the bytecode has the same difference.The problem really arises with the lack of generic arrays in Java (and Kotlin). There are two main solutions for this in Java:
Object[]
and cast the values uponget
Object[]
, but cast it toT[]
This is a problem - because the actual type of the array is
Object[]
. As a consequence, these "generic" arrays should not be exposed! I can understand why libGDX does so, namely, to improve performance and remove the small overhead of method calls, but this is definitely not safe.The fact that
get
and[]
differ may be a bug, though. Perhaps looking into this or filing a bug report may help.Java bytecode instruction list