I'm trying to create an array of the nested class Key which uses type variables/parameters from the main class BTree, but I can't get rid of the ClassCastException at runtime. I'm not very good with generics in Java, I'd appreciate if someone let me know what the issue is and how to fix it.

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

Edit:

I also tried casting Object array to Key array and it throws ClassCastException as well:

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

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

2 Answers

1
Magyar Dávid On Best Solutions

EDIT: complete rewrite

What about extracting the casting of the inner class into a wrapper type, having the inner class as its generic type parameter? The other answer suggests collections, which have backing objects anyways. If we define such an array wrapper, then the only drawback is, that when the array is being read or being written, it is through methods and not directly:

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

}

So this:

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

Becomes:

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

Of course it can be improved, like made iterable, so foreach loops will work too:

@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
Piotr Praszmo On

Arrays and generics do not mix. Just use a collection like list:

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

Performance difference is going to be negligible.

For supporting legacy code. Java offers "raw" types to circumvent generic type system when dealing with arrays. Your problem is that Node is inner class of a generic class and contains implicit generic parameters. The type is really BTree<T, V>.Node. You cannot create an array of such generic type. To create an array you need "raw" type without any generic parameters. In your case that type is 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();
    }
}

Note that this requires an unsafe cast.