3

I have two classes, AbstractArrayMyList class and a ArrayListSorted class, which extends AbstractArrayMyList.

Here is my declaration for AbstractArrayMyList and pertinent constructors

public abstract class AbstractArrayMyList<E extends Comparable<E>> implements MyList<E>  {
    private static final int DEFAULT_CAPACITY = 100; 
    protected E[] elementData;
    public AbstractArrayMyList() {
            this( DEFAULT_CAPACITY);
    }
    public AbstractArrayMyList( int capacity) {
           elementData = (E[]) new Object[capacity];
    }

with MyList being the adt interface

And my ArrayListSorted class(with pertinent constructor),

 public class ArrayListSorted<E extends Comparable<E>> extends 
       AbstractArrayMyList<E> {
       public ArrayListSorted() {
              super();
        }
}

Here is the line of code that is causing the class cast exception. (just creating an array list sorted class with bounded type Integer. Im really confused about why this exception is occurring.

ArrayListSorted<Integer> toTestInteger = new ArrayListSorted<Integer>();

chrylis explained from here, Why am i getting a class cast exception(with generics, comparable)? that the issue was that the jvm sees my new Object[capacity] as an object array. I agree with that point but that was when the definition for my AbstractArrayMyList was still

public abstract class AbstractArrayMyList<E> implements MyList<E>   

, meaning that the jvm has to treat E as on object because it knows nothing else about it. But since i added E extends Comparable shouldn't this cast be allowed? JVM will recognize this as an array of comparable objects?

Community
  • 1
  • 1
committedandroider
  • 8,711
  • 14
  • 71
  • 126

2 Answers2

0

If JVM has allowed that, you can put an Object other than E, say String, and when you retrieve the Object you assume it to be E and cast it to E which will result in ClassCastException at runtime. Generics exactly prevent this, detect as many failures as possible at compile time.

Pandiri
  • 313
  • 1
  • 4
0

The problem is Object is not Comparable. So the array creation fails in the Abstract class's constructor.

You can fix the problem by removing the Comparable portion in the abstract declaration:

 public abstract class AbstractArrayMyList<E> implements MyList<E>  {
   ...

}

And then in your subclass:

public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E> {
    ...
}

Makes everything work. No class cast exceptions.

This ends up being better anyway since before you would have restricted all subclasses of AbstractArrayMyList to be Comparable. Now it doesn't have to be.

UPDATE

I understand now that you will be resorting the elements in the abstract class. In that case, make your array of type Comparable:

public abstract class AbstractArrayMyList<E extends Comparable<E>> implements List<E>  {

    ...

    public AbstractArrayMyList( int capacity) {
         elementData = (E[]) new Comparable[capacity];
   }

   ...

Then all of your sorting will work:

For example having a main method of this:

public static void main(String... args){
    ArrayListSorted<Integer> toTestInteger = new ArrayListSorted<Integer>();

    toTestInteger.add(5);
    toTestInteger.add(2);
    System.out.println(toTestInteger.get(0));
    System.out.println(toTestInteger.get(1));

}

Will correctly print

2
5

(this assumes your add() code in your abstract class resorts the array)

UPDATE 2

If you are sorting in the child class then you will have make a few minor changes to the abstract class.

The easiest way to make this work is for the abstract class to get the array passed in via the constructor. This way, the child classes make the type of array.

public abstract class AbstractArrayMyList<E> implements List<E>  {
    protected E[] elementData;
    int size=0;

    protected AbstractArrayMyList(E[] elementData) {
         this.elementData = elementData;
    }

    ....

 }

Then in your child class, you do the default capacity stuff and then call the parent's constructor passing in your already constructed array of the correct type.

public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E> {
     private static final int DEFAULT_CAPACITY = 100; 

     public ArrayListSorted(){
         this(DEFAULT_CAPACITY);
     }

     @SuppressWarnings("unchecked")
    public ArrayListSorted(int initialSize){
           //call parent constructor passing in new array
        super((E[]) new Comparable[initialSize]);
    }

...

Now the code again works as it should

dkatzel
  • 31,188
  • 3
  • 63
  • 67
  • I tried this too but the issue is that AbstractArrayMyList is working with elementData array that contains elements that are not necessarily Comparable, because E doesn't implement Comparable in AbstractArrayMyList – committedandroider Jan 19 '15 at 05:05
  • thank you :))) And it woudn't matter for unsorted array to be working with types of Comparable? – committedandroider Jan 19 '15 at 05:17
  • It depends where you sort. Are you sorting in the abstract class or the child class? If the abstract class, then all child classes have to use `E` that are Comparable. If you are sorting in the child class, then you are back to the same problem or the array being the wrong type. – dkatzel Jan 19 '15 at 05:27
  • Updated answer again with solution for how to handle sorting only on child class – dkatzel Jan 19 '15 at 05:34
  • Sorting just in the child class sorted array, not unsorted array – committedandroider Jan 19 '15 at 06:09
  • And you can cast the array to E[] because you know thats the type of the super class, from AbstractArrayMyList? – committedandroider Jan 19 '15 at 06:19