ClassCastException when overriding the super method (Comparable)
I did search but couldn’t find a similar issue. I’m sorry if this is a duplicate. I wrote a general queue method and tried to extend it to have a priority queue. I don’t understand why I can only insert when using methods in the parent (super class), not the code in the subclass, while storage[n] is Comparable and data is Comparable. If I try to do this in a subclass, a ClassCastException will be thrown. Am I doing something wrong?
RegularQueue.java
import java.util.Arrays;
public class RegularQueue<T> {
protected int capacity;
protected T[] storage;
@SuppressWarnings("unchecked")
RegularQueue(int capacity) {
this.capacity = capacity;
storage = (T[]) new Object[this.capacity];
}
@Override
public String toString() {
return "Queue{" +
"capacity=" + capacity +
", storage=" + Arrays.toString(storage) +
'}';
}
void insert(T data) {
storage[0] = data;
}
}
PriorityQueue.java
public class PriorityQueue<T> extends RegularQueue<Comparable<T>> {
PriorityQueue(int capacity) {
super(capacity);
}
This doesn't work
@Override
void insert(Comparable<T> data) {
storage[1] = data;
}
---> This works fine.
@Override
void insert(Comparable<T> data) {
super.insert(data);
// }
public static void main(String[] args) {
PriorityQueue<Integer> q = new PriorityQueue<>(5);
q.insert(1);
System.out.println(q.toString());
}
}
Solution
You see this ClassCastExpression because in RegularQueue you are using the untyped safe assignment storage = (T[]) new Object[this.capacity]
<. >
regular queue as a type parameter of T
. Therefore, at compile time it is known that this T
must be Compatible
or its subtype at runtime. Therefore, the compiler emits Comparable[
] and forces this in PriorityQueue every time it accesses T[] storage
.
The problem now is that storage
is not actually of type T
[], but only of type Object[]
, which leads to the ClassCastException you see. This happens when the field is accessed in any way, even if storage.length
triggers it.
The
reason you don’t see this exception in insert is that the method call
super.insert
does not access storage directly.Only the super implementation does this, but does not perform any conversions, because the RegularQueue internal type T
is unknown at compile time.
The solution is not to declare storage
as T[], but to use Object[]
because this is the actual type.
Others reported this to the JDK team as a bug, but the report was resolved (as expected) as “not an issue”. However, one of the JDK developers, Stuart Marks, was in delves into (probably better than this answer) the underlying problem in the report. I highly recommend reading it.