【问题标题】:LibGDX Array's internal array is not type safeLibGDX Array 的内部数组不是类型安全的
【发布时间】:2020-08-09 16:17:54
【问题描述】:

我正在以通用方式从 LibGDX 扩展 Array extends Array<MyClass> 但是当我直接访问它的 items 数组时,我得到一个错误 Object 无法转换为类 MyClass

我不明白为什么会出现此错误。

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

我还注意到,如果我尝试用 items[2] 做类似的事情,会抛出同样的错误,而 get(2) 没有问题...

编辑:这是我从 LibGDX 使用的 Array Array

【问题讨论】:

  • @Zabuzard Array 来自 LibGDX,如标签所示。我会把它添加到标题中。我通常将框架名称放在标题中,但有很多人对其进行编辑和删除....然后就会发生这样的事情。
  • 我对@9​​87654334@ 类不太了解,但它的内部items 数组实际上是Object[] 类型而不是T[],而它的方法如get维护泛型的类型安全。因此,直接使用items 将返回Object,可以安全地转换为T。因此,您只需向MyClass 添加一个演员表,这将是安全的。但是你绝对不应该直接使用items,而是使用类提供的仍然保持类型安全的方法。
  • @Zabuzard 它看起来确实是 T 类型,它被列为 public T[] items; libgdx.badlogicgames.com/ci/nightlies/docs/api/com/badlogic/gdx/… 我正在扩展数组,因为我喜欢直接访问这些项目。通常一切正常,但我不知道为什么会抛出这个错误。似乎没有意义。
  • 文档可能不准确或来自不同的版本,您是否查看了实际的源代码?
  • @Zabuzard 是的,我知道,我说过我自己从源代码public T[] items; 复制了它,所以它确实是这么说的。我还给了你那个链接,所以你可以检查数组。如果你想要源代码,你可以在这里看到它:github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/…

标签: java arrays libgdx


【解决方案1】:

说明

其根本原因是,在 Java 中,泛型在运行时被删除。所以LibGDX的T[] items,编译后就是一个普通的Object[]

因此,虽然您的代码确实可以编译,因为您使用了正确的类型,但当您运行它时,Java 会检测到一个可能的问题,因为您试图将 Objects 视为 MyClass。因为,在运行时,数组只是一个Object[],它的所有内容都是Object。所以泛型在运行时不能保持活动状态。


反射

真正拥有true T[] 的唯一方法是通过反射动态地创建它,并使用实际的真实类型(以令牌形式给出)。 LibGDX 为此提供了一个构造函数:

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

这样,数组在运行时也将是T[] 类型。它实际上也在source code中评论了这一点:

提供对底层数组的直接访问。如果 Array 的泛型类型不是 Object,则只有在使用 Array#Array(boolean, int, Class) 构造函数时才能访问此字段。


get

get 调用有效,因为在这种情况下,Java 足够聪明,可以确定擦除实际上是安全的。因此,虽然MyClass foo = get(index); 确实衰减为MyClass foo = (MyClass) get(index);,但Java 知道这是一个安全转换

在您直接使用数组的示例中,Java 无法解决这个问题并失败了。有关确切的详细信息,您可能必须深入了解 JLS。


items.length

在您的子班级中以任何方式使用items 都会立即引发问题,所以即使是这个看起来很无辜的sn-p:

int size = items.length;

这是 Java 通用系统的一个非常技术性的边缘案例和限制。您的子类在使用 items 时需要一个 MyClass[],但在运行时它会得到一个 Object[],这不是它想要的。所以它会触发错误。


另一个例子

这是另一个无需使用任何 LibGDX 即可重现问题的最小示例。您可以轻松重现所看到的情况。

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

【讨论】:

  • 这并不能解释为什么items.length 也会抛出cannot be cast to class 错误。
  • @Hasen 添加了一个部分
  • @Hasen 欢迎您。如果您花几个小时阅读 JLS,您可能会找到发生这种情况的确切原因。不确定这是否值得——这只是 Java 通用系统的一个非常具体的技术限制。今天之前我什至不知道这一点,尤其是items.length很有趣。
  • 是的,这很奇怪。但是我有一个解决方法也可以完成这项工作,所以至少没问题。
猜你喜欢
  • 2013-06-04
  • 2011-12-17
  • 1970-01-01
  • 2019-01-09
  • 1970-01-01
  • 1970-01-01
  • 2015-09-26
  • 2013-08-01
  • 2013-12-26
相关资源
最近更新 更多