0

I have the following code:

public abstract class Heap {

    Comparable<?> data[];

    int count, size;

    public Heap( int size ) {

        this.size = size;

        data = new Comparable<?>[ size + 1 ];

        this.count = 0;
    }

    public abstract void insert( Comparable<?> item );

}

class MinHeap extends Heap {

    public MinHeap (int size ) { super(size); }

    public void insert( Comparable<?> item ) {

        //this line here is giving me an error 
        //due to how I am storing the array in Heap

        int k = data[ 0 ].compareTo(  item );

    }
}

The line indicated above is giving me this error: The method compareTo(capture#1-of ?) in the type Comparable<capture#1-of ?> is not applicable for the arguments (Comparable<capture#2-of ?>). I cannot figure out a way to make it work while maintaining these conditions: 1) I want the MinHeap to work with any data that implements Comparable, 2) I do NOT want to pass a pre-initialized array into the constructor. I say this because I do not want to do the following:

abstract class Heap< T extends Comparable<T> > {

       T data[];

       public Heap( T data[], int size ) {

             this.data = data; 
    //I do not want to have to pass an instantiated array. 
    //I want the constructor to handle the instantiation. If I do this I know the issue with the 
    //compareTo will be solved, but I really prefer to avoid this.
       }

}

My question is this: In my code, why am I getting this error? Does anyone know a way besides the way that is described in the second example? I would like to be able to create a min heap data structure with any comparable data. All helpful comments are appreciated. Thank you.

Side note: do not worry about the access modifiers of the instance variables. I left them as default for simplicity. I do know that they should be private with setters/getters or protected.

Rob L
  • 2,650
  • 3
  • 25
  • 47
  • The second solutions seems to be the way to go. You could try something like this (but I don't know if it is possible like this): data = (T[]) Array.newInstance(data.getClass().getComponentType(), size); – George Apr 13 '14 at 17:11

3 Answers3

1

First of all, this code is invalid for creating a generic array:

data = new Comparable<?>[ size + 1 ];

This link in the Java Trails explains why it's illegal, but it boils down to the fact that arrays must know their type at compilation, and generics work based off of type erasure and can be inferred at runtime.

But before we can fix that, there's an issue with your generics - they're not really...generic. You're only using the wildcard generic here with no bounds.

If you want to have your abstract class with a generic array that is full of Comparable, then you want to have your abstract class with a bound to Comparable<T>, and have your data simply be bound to T. With this, we can finally fix the array initialization into a compilable (but unchecked cast) form:

data = (T[]) new Comparable[size + 1];

Here's the full class for reference. It's close to your second form, and doesn't require that you pass in an instantiated array. Further, since T is bound to Comparable<T>, we don't need to declare it as an argument in the method - we can simply provide T.

public abstract class Heap<T extends Comparable<T>> {
    T data[];
    int count, size;

    public Heap(int size) {
        this.size = size;
        data = (T[]) new Comparable[size+1];
        this.count = 0;
    }

    public abstract void insert(T item);

}

Further to this example, you would also want to add the generic type to your subclass as well:

class MinHeap<T extends Comparable<T>> extends Heap<T>    
Makoto
  • 96,408
  • 24
  • 164
  • 210
  • Is it best practice to check the cast from comparable to T? – Rob L Apr 13 '14 at 17:18
  • It's a bit tricky to do that, since you don't know what type `T` is until runtime. It may be an unchecked cast, but it's known that this is the way to create generic arrays. If you could, why not use a `List` instead? – Makoto Apr 13 '14 at 17:19
1

Try this one:

  • First compareTo() return int not boolean value.
  • public abstract void insert( Comparable<?> item ); is wrong.
  • Use List in case of generic instead of static array. For more info read How to create a generic array?

Sample code:

abstract class Heap<T> {
    List<Comparable<T>> data;

    public Heap(int size) {
        data = new ArrayList<Comparable<T>>();
    }

    public abstract void insert(T item);
}

class MinHeap<T extends Comparable<T>> extends Heap<T> {

    public MinHeap(int size) {
        super(size);
    }

    public void insert(T item) {
        int k = data.get(0).compareTo(item);
    }
}
Community
  • 1
  • 1
Braj
  • 44,339
  • 5
  • 51
  • 69
0

Your data can contain any kind of object, as long as its class implements Comparable. So you could have Strings, Integers, Longs, or Bananas inside your array.

And comparing an Integer with a String or with a Banana doesn't make sense. That's why the compiler doesn't let you compile this code.

The second way is the right way. You can use an array of objects internally, and cast each object to a T. If all your methods only accept instances of T, then the casts are guaranteed to succeed. Or you can use a List instead, which is much more generic-friendly than arrays.

JB Nizet
  • 633,450
  • 80
  • 1,108
  • 1,174