LibGDX Array's internal array is not type safe

穿精又带淫゛_ 提交于 2021-02-11 07:50:05

问题


I am extending Array from LibGDX in a generic fashion which extends Array<MyClass> but when I access its items array directly, I get an error that Object can not be cast to class MyClass.

I don't see exactly why I'm getting an error with this.

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);
    }
}

I noticed also that if I try to do a similar thing with items[2] throws the same error while get(2) has no problem...

EDIT: Here is the Array I used from LibGDX Array


回答1:


Explanation

The underlying cause of this is that, in Java, generics are erased at runtime. So the T[] items of LibGDX, after compilation, is just a plain Object[].

So while your code actually does compile because you used the correct type, when you run it, Java detects a possible issue because you are trying to treat Objects as MyClass. Since, again, at runtime, the array is just a Object[] and all its contents are Object. So the generics cant be kept alive during runtime.


Reflection

The only way to actually have a true T[] is to create it dynamically via reflection with the actual real type, given as token. LibGDX offers a constructor for that:

public Array (boolean ordered, int capacity, Class arrayType) {
    this.ordered = ordered;
    items = (T[]) ArrayReflection.newInstance(arrayType, capacity);
}

That way, the array will also at runtime be of type T[]. It actually also commented this in the 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

The get call works because in this situation Java is smart enough to figure out that the erasure is actually safe. So while MyClass foo = get(index); indeed decays to MyClass foo = (MyClass) get(index);, Java knows that this is a safe cast.

In your example where you use the array directly, Java can not figure that out and fails. For the exact details you probably have to dig into the JLS.


items.length

Using items in any way in your child class will immediatly trigger the problem, so even this innocent looking snippet:

int size = items.length;

This is a very technical edge case and limitation of Javas generic system. Your child class expects a MyClass[] when using items but it gets an Object[] at runtime, which is not what it wants. So it triggers the error.


Another example

Here is another minimal example that reproduces the issue without using any LibGDX. You can easily reproduce the situation as seen.

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
        }
    }
}


来源:https://stackoverflow.com/questions/63323421/libgdx-arrays-internal-array-is-not-type-safe

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!