It's not type safe because of the primitive Class objects. For example I can create a new Stack in the following manner:
new Stack<Boolean>(boolean.class, 10);
Which is OK with the compiler but throws an exception because boolean.class
is a Class<Boolean>
and boolean[]
cannot be cast to Boolean[]
.
The alternative you show commented out:
array = (T[])new Object[size];
Is actually somewhat type safe but for a different reason: it is due to erasure. You cannot, for example, cast a new Object[size]
to a Number[]
, but the cast never happens on the array. It happens some time later, like when you return an element of the array from a method (in which case the element is casted). If you tried to do something like return the array to outside the object it will throw an exception.
Usually the solution is not to generically type the array. Instead, do something like this:
class Stack<E> {
Object[] array = new Object[10];
int top;
void push(E elem) {
if(top == array.length)
array = Arrays.copyOf(array, array.length * 2);
array[top++] = elem;
}
E pop() {
@SuppressWarnings("unchecked")
E elem = (E)array[--top]; // type safe cast
array[top] = null;
return elem;
}
}
The above cast is type safe because you can only push an E
in to the array. Both the JDK Stack (which extends Vector) and ArrayList work this way.
If you want to use newInstance
, you would have to reject primitives as there is no way to represent them generically:
Stack(Class<T> tClass, int size) {
if(tClass.isPrimitive())
throw new IllegalArgumentException();
// ...
}