2

I'm writing a simple implementation of a generic max heap. If I write

public class FastMaxHeap<T>{

  T[] data;
  int size;

  static final int HEAP_SIZE = 10000;

  @SuppressWarnings("unchecked")
  public FastMaxHeap(){
    data = (T[]) new Object[HEAP_SIZE];
  size = 0;
  }
}

it compiles. Now to actually implement the heap, i.e. write maxHeapify(), I need to be able to compare two T's. One option that a priori seems possible would be to tell the compiler that T implements Comparable. But if I type replace < T > with < T implements Comparable > the compiler complains -- how can I do this?

Alternatively, I could define a class

public class HasValue{

  int value;

  public HasValue(int value){
        this.value = value;
  }

}

and in theory I should then be able to compare two HasValue objects like x.value > y.value. But if I type

public class FastMaxHeap<T extends HasValue>{

  T[] data;
  int size;

  static final int HEAP_SIZE = 10000;

  @SuppressWarnings("unchecked")
  public FastMaxHeap(){
    data = (T[]) new Object[HEAP_SIZE];
  size = 0;
  }
}

I now get a ClassCastException. What is going on here? Java generics hurt my brain.

andyInCambridge
  • 1,065
  • 2
  • 12
  • 25

4 Answers4

5

In the first case T extends Object which is erased to Object at runtime.

In the second case T extends HasValue is erased to HasValue so you need to have.

data = (T[]) new HasValue[HEAP_SIZE];

IMHO It is needlessly pedantic that Java doesn't allow new T[HEAP_SIZE] to do what you have to do anyway.

Peter Lawrey
  • 498,481
  • 72
  • 700
  • 1,075
  • 1
    I deleted my answer in favor of yours. – mikeslattery Dec 19 '12 at 20:35
  • @stefanbachert The casting will work provided the component type of the new array is a sub-type of what T extends. – Peter Lawrey Dec 19 '12 at 20:41
  • It's not "needlessly pedantic"; it's very important. Try to return `data` from a method in your class with return type `T[]` and see what happens. – newacct Dec 20 '12 at 00:49
  • @newacct Can you be more specific? – Peter Lawrey Dec 20 '12 at 09:01
  • I am assuming you mean you want Java to allow `new T[HEAP_SIZE]` to mean `(T[])new HasValue[HEAP_SIZE];`. But that is unsafe so it is important to force the user to have to explicitly override it. – newacct Dec 20 '12 at 09:43
  • @newacct I am sure that makes sense to a degree but if *very* few people understand the distinction I suspect you haven't gained anything. – Peter Lawrey Dec 20 '12 at 10:12
0

You may try this one (not compiled, yet)

public class FastMaxHeap<T extends HasValue>{

  HasValue[] data;
  int size;

  static final int HEAP_SIZE = 10000;

   public FastMaxHeap(){
     data = new HasValue[HEAP_SIZE];
     size = 0;
   }
}
stefan bachert
  • 8,643
  • 3
  • 31
  • 38
0

It is better to have type token to create arrays like this

public class FastMaxHeap<T>{

  T[] data;
  int size;

  static final int HEAP_SIZE = 10000;

  @SuppressWarnings("unchecked")
  public FastMaxHeap(Class<T> clazz){
    data = (T[])Array.newInstance(clazz, HEAP_SIZE);
    size = 0;
  }
}

In such way you will have no ClassCastExceptions in runtime

And also: < T implements Comparable > is not correct, correct one is < T extends Comparable >

maks
  • 5,574
  • 17
  • 72
  • 116
0

Your heap should accept a Comparator< T > as a constructor argument. Problem solved. Client can use any type he wants. You can also supply a simple overload which infers the comparator implementation for types T that already implement Comparable.

Judge Mental
  • 5,091
  • 15
  • 20