2

This code works correctly and print out number 120. However, if I uncomment the line in the main function.

The code is throwing an exception: Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Comparable; cannot be cast to [Ljava.lang.Double; at General.main

I searched and found several discussion on generics array in java. People acknowledged the treatment of generics array is not very well in java. But I still feel very bad about this code: print out a line in different places will have different consequences, (execution correctly versus throw an exception).

Could someone please comment on this code and advise if there is any simple fix to the exception related to the println code in the main function?

Here is my code :

public class General<Key extends Comparable<Key>> {
    public Key[] keys;      
    @SuppressWarnings("unchecked")
    public General(int NMAX) {
        keys = (Key[]) new Comparable[NMAX];
        System.out.println(keys.length);
    }
    public static void main(String[] args){
    General<Double> g = new General<Double>(120);
    //      System.out.println(g.keys.length);
    }
}


System.out.println(g.keys.length);
Sulthan Allaudeen
  • 11,330
  • 12
  • 48
  • 63
Chao
  • 33
  • 5
  • Is there some reason you cannot use a `List` instead of an Array? That is, something like `private List keys = new ArrayList();` - then you could make the class `General` – Elliott Frisch Mar 03 '14 at 20:43
  • @Elliott's inquiry is pretty reasonable. The short answer is the example snippet isn't very type safe. The Lists will take care of this for you. – Radiodef Mar 03 '14 at 20:48
  • ArrayList will surely solve the problem, but I am looking for deeper understanding of why my approach failed, as explained by people below. – Chao Mar 03 '14 at 23:13

2 Answers2

2

The reason you get the exception in a seemingly strange place is because of erasure. Erasure of Key is Comparable so the cast here actually never happens at run time:

keys = (Key[]) new Comparable[NMAX];

But as you've observed, a cast does happen when you retrieve the array from outside the object. A Comparable[] is not a Double[] so you get the exception. As long as you don't access the array outside the class, the way you're doing it probably won't cause a problem.

The "safer" idiom is to make the array non-generic by making its declaration a Comparable[]. Then you:

  • Ensure that only Key can be put in the array.
  • Make the unchecked cast on a retrieval call.

Something like this:

@SuppressWarnings("rawtypes")
public class General<Key extends Comparable<Key>> {
    private Comparable[] keys;

    public General(int NMAX) {
        keys = new Comparable[NMAX];
    }

    public void set(int ind, Key k) {
        keys[ind] = k;
    }

    @SuppressWarnings("unchecked")
    public Key get(int ind) {
        return (Key)keys[ind];
    }
}

That's type safe and it's the way the JDK does it.

Another way is to have the client provide the array:

public class General<Key extends Comparable<Key>> {
    private Key[] keys;

    public General(Key[] keys) {
        this.keys = keys;
    }
}

You should also consider using a List instead because they do all this stuff for you and are fully generic. You can just do new ArrayList<Key>().

Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Thanks a lot for clarification here. I was following the algorithm courses from Prof SedgeWick and trying not to use complicated generics to achieve simple stuff.:) – Chao Mar 03 '14 at 23:10
1

You could try to keep the keys as a Comparable[] array.

public class General<Key extends Comparable<Key>> {
public Comparable[] keys;      // keys[i] = priority of i

public General(int NMAX) {
    keys = new Comparable[NMAX];
    System.out.println(keys.length);
}

public static void main(String[] args) {
    General<Double> g = new General<Double>(120);
    System.out.println(g.keys.length);
}
}
mihaic
  • 135
  • 1
  • 7