I have written a generic class and below is the constructor of the class. I want to do something like this as written in line
elements = (E[])new Object[siz
I believe the usual way is to pass the Class
into the constructor, and use Array.newInstance(Class<?>, int...) like
public Stack(Class<E> cls, int size){
if(size <0){
throw new IllegalArgumentException("Initial capacity cannot be "
+ "negative or zero");
}
elements = (E[]) Array.newInstance(cls, size);
}
Edit
From your update, please don't use raw-types. With Java 7 and above you can use the diamond operator <> like
Stack<Integer> st = new Stack<>();
st.push(ran.nextInt(100));
With earlier versions you specify the generic type like
Stack<Integer> st = new Stack<Integer>();
st.push(ran.nextInt(100));
Here is the most-minimal code necessary to reproduce your exception.
class Stack<E> {
protected E[] elements = (E[])new Object[1];
}
class IntStack extends Stack<Integer> {
void push(Integer i) {
// subtly accessing elements as Integer[] which it's not
elements[0] = i;
}
}
Java generics are implemented with type erasure so after compilation, this code translates to something like this:
class Stack {
protected Object[] elements = new Object[1];
}
class IntStack extends Stack {
void push(Integer i) {
// throws ClassCastException
((Integer[])elements)[0] = i;
}
}
Clearly a new Object[]
is not an Integer[]
. Notice how the cast gets moved to somewhere you did not explicitly put it. This is why (E[])new Object[size]
was an unchecked cast and displayed a warning.
Instead, you should use Object[]
and perform the unchecked cast only when you need to return an element to the outside world.
class Stack<E> {
private Object[] elements;
private int size;
Stack(int len) {
elements = new Object[len];
}
void push(E e) {
elements[size] = e;
size++;
}
E pop() {
@SuppressWarnings("unchecked");
E e = (E)elements[size - 1];
size--;
return e;
}
}
It's clear now. You're trying to create your Stack
without generic type. Consider Stack<Integer> st = new Stack<>();
instead.
Here is how you would fix it, you should not ever do (T[]) new Object[DEFAULT_CAPACITY];
instead an abstraction should be there for example (T[]) new Comparable[DEFAULT_CAPACITY];
public class ArrayStack<T extends Comparable<? super T>> implements Stack<T> {
private final int DEFAULT_CAPACITY = 50;
private int top;
private T[] elements;
@SuppressWarnings("unchecked")
public ArrayStack() {
this.elements = (T[]) new Comparable[DEFAULT_CAPACITY];
this.top = 0;
}
}
Basically, when you do (E[])new Object[size]
, it is a lie. The object's actual runtime class is Object[]
, which is not a subtype of E[]
for whatever E
is (unless E
is Object
). So the cast is, theoretically, incorrect. However, this does not create any immediate problems because inside the Stack
class, E
is erased to its upper bound, in this case Object
. So inside the Stack
class, we can use elements
as E[]
, and put E
in and get E
out of it, with no problem.
A problem only occurs when the (incorrect) fact that elements
is type E[]
is "exposed" to the outside of the class, outside of the scope of the erasure of E
, into a scope where someone has a concrete type argument for E
. This usually happens when someone inadvertently makes elements
public, or implements a method that returns it to the outside like
E[] getElements() {
return elements;
}
Then on the outside of the class, someone has a Stack<SomeSpecificType>
, and call this method, and expect a SomeSpecificType[]
, which is not what it gets.
However, your Stack
class does not have such a method. So how are you "exposing" elements
? The answer is that elements
is protected
, and is therefore "exposed" to subclasses. In this case, the subclass, MinMaxStack
, extends Stack
with a specific type for E
, therefore, it "sees" elements
as a specific type of array, which it is not.