ClassCastException при попытке создать массив класса с переменными типа

Я пытаюсь создать массив вложенного класса Key, который использует переменные/параметры типа из основного класса BTree, но не могу избавиться от ClassCastException во время выполнения. Я не очень хорошо разбираюсь в дженериках в Java, я был бы признателен, если бы кто-нибудь сообщил мне, в чем проблема и как ее исправить.

public class BTree<T extends Comparable<T>, V> {
   //...
   private class Node {
        public int n;
        public boolean isLeaf = false;
        public Key[] keys = (Key[]) new Comparable[2 * MIN_DEGREE - 1]; //ClassCastException
        public Node[] children = (Node[]) new Object[2 * MIN_DEGREE];
    }

    private class Key implements Comparable<Key> {
        public T key;
        public V val;

        public Key(T key, V val) {
            this.key = key;
            this.val = val;
        }

        public boolean lessThan(Key that) {
            return this.key.compareTo(that.key) < 0;
        }

        public boolean greaterThan(Key that) {
            return this.key.compareTo(that.key) > 0;
        }

        @Override
        public int compareTo(Key that) {
            if (this.lessThan(that)) return -1;
            if (this.greaterThan(that)) return 1;
            return 0;
        }
    }
    //....
}

Редактировать:

Я также пробовал преобразовать массив Object в массив Key, и он также выдает ClassCastException:

public Key[] keys = (Key[]) new Object[2 * MIN_DEGREE - 1]; 

И когда я создаю массив Key без приведения, при компиляции выдает ошибку Generic array creation:

public Key[] keys = new Key[2 * MIN_DEGREE - 1]; 

Вы пытаетесь преобразовать массив Comparables в массив Keys. Это похоже на преобразование множества млекопитающих (верблюдов, лошадей и кошек) в множество собак. Зачем вообще это делать? Создайте массив Key и массив Node.

RealSkeptic 12.05.2019 17:13

@RealSkeptic Я получаю общую ошибку создания массива, когда создаю массив ключей new Key[size]

razz 12.05.2019 17:17

Да, потому что дженерики и массивы несовместимы. Я не уверен, что вы пытаетесь здесь сделать, но почему вы не используете списки?

RealSkeptic 12.05.2019 17:26

Ошибки создания универсального массива можно решить, создав массив объектов и приведя его к запрошенному универсальному типу. например T[] myArray = (T[]) новый объект[10]; Также проверьте другие обсуждения по этому поводу, например. stackoverflow.com/questions/17831896/…

Konrad Neitzel 12.05.2019 17:29

@RealSkeptic Я реализую B-деревья из книги CLRS, и в ней используются массивы, также я думаю, что использование списков может повлиять на время выполнения (хотя не уверен) таких операций, как поиск и вставка.

razz 12.05.2019 17:29
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
5
69
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

РЕДАКТИРОВАТЬ: полная перезапись

Как насчет извлечения приведения внутреннего класса к типу-оболочке, имеющего внутренний класс в качестве параметра универсального типа? Другой ответ предлагает коллекции, которые в любом случае имеют вспомогательные объекты. Если мы определим такую ​​оболочку массива, то единственный недостаток в том, что когда массив читается или записывается, это происходит через методы, а не напрямую:

public class Outer<A,B> {
    static final int SIZE = 10;

    public Outer() {
        Inner1 innerInstance = new Inner1();
    }

    private class Inner1 {
        //Inner2[] inner2array = new Inner2[SIZE];
        Array<Inner2> inner2array = new Array<>(SIZE);
    }

    private class Inner2 {
        A a;
        B b;
    }

    public static void main(String[] test) {
        Outer outerInstance = new Outer();
    }

    private static class Array<T> {
        private Object[] values;

        public Array(int size) {
            values = new Object[size];
        }

        @SuppressWarnings("unchecked")
        public T get(int index) {
            return (T) values[index];
        }

        public void set(int index, T value) {
            values[index] = value;
        }

        public int length() {
            return values.length;
        }
    }

}

Итак, это:

public Key[] keys = new Key[2 * MIN_DEGREE - 1];

Становится:

public Array<Key> keys = new Array<>(2 * MIN_DEGREE - 1); 

Конечно, его можно улучшить, например сделать итерируемым, поэтому циклы foreach тоже будут работать:

@Override // implements Iterable<T>
public Iterator<T> iterator() {
    return new Iterator<T>() {
        int index = 0;

        @Override
        public boolean hasNext() {
            return index < values.length;
        }

        @Override @SuppressWarnings("unchecked")
        public T next() {
            T current = (T) values[index];
            ++index;
            return current;
        }
    };
}

Это не идеальное решение, это своего рода хак, но оно работает. Я думаю, что это разумное решение, и мне оно нравится. Спасибо.

razz 13.05.2019 23:04

Массивы и дженерики несовместимы. Просто используйте коллекцию, например список:

public List<Node> children = new ArrayList<Node>();

Разница в производительности будет незначительной.

Для поддержки устаревшего кода. Java предлагает «сырые» типы, чтобы обойти систему общих типов при работе с массивами. Ваша проблема в том, что Node является внутренним классом универсального класса и содержит неявные общие параметры. Тип действительно BTree<T, V>.Node. Вы не можете создать массив такого универсального типа. Чтобы создать массив, вам нужен «сырой» тип без каких-либо общих параметров. В вашем случае это тип BTree.Node:

class BTree<T> {
    static final int MIN_DEGREE = 1;
    private class Node {
        public Node[] children = (Node[]) new BTree.Node[2 * MIN_DEGREE];
    }
    Node node = new Node();
    public static void main(String[] args) {
        new BTree();
    }
}

Обратите внимание, что для этого требуется небезопасное приведение.

Спасибо за ваш ответ, но ArrayLists - это динамические массивы в java, и амортизированное постоянное время для вставки усложнило бы мне задачу. Мне нужна фактическая вставка и индекс постоянного времени.

razz 13.05.2019 23:02
Ответ принят как подходящий

And when I create Key array without casting it gives Generic array creation error when compiling:

public Key[] keys = new Key[2 * MIN_DEGREE - 1];

Причина этого в том, что Key является внутренним классом внутри универсального класса BTree<T, V>, поэтому, когда вы пишете Key отдельно внутри BTree, это неявно означает BTree<T, V>.Key, и это параметризованный тип, и вам не разрешено создавать массив параметризованный тип.

Когда люди хотят создать массив параметризованного типа, одним из решений является создание массива необработанного типа, а затем приведение его к типу «массив параметризованного типа». Сложный вопрос здесь заключается в том, как написать необработанный тип. Это не Key сам по себе, так как это параметризованный тип (поскольку он неявно определяется параметризованным внешним типом). Вместо этого вы должны явно квалифицировать его с помощью необработанного внешнего типа, чтобы записать необработанный тип внутреннего класса:

public Key[] keys = (Key[])new BTree.Key[2 * MIN_DEGREE - 1];

Другие вопросы по теме