尝试创建具有类型变量的类的数组时发生ClassCastException

时间:2019-05-12 15:11:42

标签: java arrays generics

我正在尝试创建一个嵌套类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]; 

3 个答案:

答案 0 :(得分:1)

编辑:完全重写

将内部类作为其泛型类型参数提取内部类的类型转换为包装器类型怎么样?另一个答案是建议集合,它们始终具有支持对象。如果我们定义了这样的数组包装器,那么唯一的缺点是,在读取或写入数组时,它是通过方法而不是直接进行的:

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

答案 1 :(得分:1)

数组和泛型不能混合使用。只需使用列表这样的集合即可:

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

请注意,这需要不安全的转换。

答案 2 :(得分:0)

  

当我创建Key数组而不进行转换时,编译时会出现Generic array creation错误:

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

这样做的原因是Key是泛型类BTree<T, V>中的一个内部类,因此当您在Key中单独编写BTree时,它隐含了{ {1}},这是一个参数化类型,不允许创建参数化类型的数组。

当人们想要创建参数化类型的数组时,一种解决方案是创建原始类型的数组,然后将其强制转换为“参数化类型的数组”类型。这里棘手的问题是如何编写原始类型。它本身不是BTree<T, V>.Key,因为它是参数化类型(因为它被参数化外部类型隐式限定)。相反,您必须使用原始外部类型显式地限定它,以便编写内部类的原始类型:

Key