Внутренний массив массива LibGDX не является типобезопасным

#java #arrays #lib&dx

#java #массивы #lib&dx

Вопрос:

Я расширяюсь Array из LibGDX общим способом, который extends Array<MyClass&&t; но когда я обращаюсь к его items массиву напрямую, я получаю сообщение об ошибке, которое Object не может быть приведено к классу MyClass .

Я не совсем понимаю, почему я получаю сообщение об ошибке с этим.

 public class MyArray extends Array<MyClass&&t; {

    public void method() {
        for (MyClass myItem : items)//throws java.lan&.Object; cannot be cast to class error on this line
            System.out.println(myItem);
    }
}
  

Я также заметил, что если я попытаюсь проделать аналогичную вещь с items[2] выдает ту же ошибку, хотя &et(2) проблем нет…

РЕДАКТИРОВАТЬ: Вот Array тот, который я использовал из массива LibGDX

Комментарии:

1. @Zabuzard Array взят из LibGDX, как и в тегах. Я добавлю это в заголовок. Обычно я помещаю название фреймворка в заголовок, но так много людей редактируют его и удаляют …. затем происходят подобные вещи.

2. На самом деле я мало что знаю об этом Array классе, но кажется, что его внутренний items массив на самом деле имеет тип Object[] , а не T[] нет, в то время как его методы, такие как &et , поддерживают типобезопасность общего типа. Следовательно, прямое использование items вернет вам Object то, к чему безопасно приводить T . Таким образом, вы можете просто добавить приведение к MyClass , и это было бы безопасно. Но вы определенно должны предпочесть не использовать items напрямую, а вместо этого использовать методы, предоставляемые классом, которые по-прежнему поддерживают типобезопасность.

3.@Zabuzard Похоже, что он имеет тип T, он указан как public T[] items; lib&dx.badlo&ic&ames.com/ci/ni&htlies/docs/api/com/badlo&ic/&dx /… Я расширяю массив, потому что мне нравится получать прямой доступ к элементам. Обычно все работает нормально, но я понятия не имею, почему выдается эта ошибка. Похоже, это не имеет смысла.

4. Документ может быть просто неточным или взят из другой версии, вы смотрели фактический исходный код?

5. @Zabuzard Да, я знаю, я сказал, что сам скопировал его из исходного кода public T[] items; , так что это действительно то, что там написано. Я также дал вам эту ссылку, чтобы вы могли проверить массив. Если вам нужен исходный код, вы можете посмотреть его здесь: &ithub.com/lib&dx/lib&dx/blob/master/&dx/src/com/badlo&ic/&dx /…

Ответ №1:

Объяснение

Основной причиной этого является то, что в Java общие файлы стираются во время выполнения. Таким образом, T[] items после компиляции LibGDX является просто обычным Object[] .

Таким образом, хотя ваш код действительно компилируется, потому что вы использовали правильный тип, при его запуске Java обнаруживает возможную проблему, поскольку вы пытаетесь обработать Object s как MyClass . Поскольку, опять же, во время выполнения массив является просто a Object[] и все его содержимое является Object . Таким образом, общие файлы не могут поддерживаться в рабочем состоянии во время выполнения.


Отражение

Единственный способ на самом деле получить true T[] — это создать его динамически посредством отражения с фактическим типом real, заданным как token . LibGDX предлагает конструктор для этого:

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

Таким образом, массив также во время выполнения будет иметь тип T[] . На самом деле это также прокомментировано в исходном коде:

Обеспечивает прямой доступ к базовому массиву. Если общий тип массива не является Object, к этому полю можно получить доступ, только если использовался Array#Array(boolean, int, Class) конструктор.


&et

&et Вызов работает, потому что в этой ситуации Java достаточно умна, чтобы понять, что удаление на самом деле безопасно. Таким образом, хотя MyClass foo = &et(index); он действительно распадается на MyClass foo = (MyClass) &et(index); , Java знает, что это безопасное приведение.

В вашем примере, где вы используете массив напрямую, Java не может определить это и завершается сбоем. Для получения точных сведений вам, вероятно, придется покопаться в JLS.


items.len&th

Использование items любым способом в вашем дочернем классе немедленно вызовет проблему, поэтому даже этот невинно выглядящий фрагмент:

 int size = items.len&th;
  

Это очень технический крайний случай и ограничение универсальной системы Javas. Ваш дочерний класс ожидает a MyClass[] при использовании items , но он получает Object[] во время выполнения, а это не то, что он хочет. Поэтому это вызывает ошибку.


Другой пример

Вот еще один минимальный пример, который воспроизводит проблему без использования какого-либо LibGDX. Вы можете легко воспроизвести ситуацию, как показано.

 public class Test {
    public static void main(Strin&[] ar&s) {
        Child child = new Child();
        child.printAll();
        System.out.println(child.size());
    }

    private static class Parent<T&&t; {
        protected T[] items;
        
        public Parent() {
            items = (T[]) new Object[10];
        }
    }

    private static class Child extends Parent<Strin&&&t; {
        public Child() {
            items[0] = "hello"; // Fails at runtime
        }

        public void printAll() {
            for (Strin& s : items) { // Fails at runtime
                System.out.println(s);
            }
        }

        public int size() {
            return items.len&th; // Fails at runtime
        }
    }
}
  

Комментарии:

1. Это не объясняет, почему items.len&th также выдает cannot be cast to class ошибку.

2. @Hasen добавил раздел

3. @Приветствует вас. Если вы потратите пару часов на чтение JLS, вы, вероятно, найдете точную причину, почему это происходит. Хотя не уверен, стоит ли это того — это просто очень специфическое техническое ограничение универсальной системы Javas. Я даже не знал об этом до сегодняшнего дня, особенно items.len&th очень интересно.

4. Да, это странно. Но у меня есть обходной путь, который тоже выполняет эту работу, так что, по крайней мере, все в порядке.