Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a type safe generic array in java?

I want to create a generic array in java maintaining the type safety usually offered by Java.

I am using this code :

class Stack<T> { 

private T[] array = null;
public Stack(Class<T> tClass, int size) {
   maximumSize = size;
   // type unsafe
   //array = (T[])new Object[maximumSize];

   this.array = (T[])java.lang.reflect.Array.newInstance(tClass,maximumSize);
}

is this code type safe? ans if so, why? why if it is type safe I need a cast?

like image 539
Giuseppe Pes Avatar asked Dec 11 '22 08:12

Giuseppe Pes


2 Answers

The Array.newInstance(..) method has a return type of Object. As such, you cannot directly assign it to anything other than Object. You therefore need a cast.

The method delegates to a native method which

Creates a new array with the specified component type and length

Therefore it is creating an array of type T.

The type safety, assuming array is declared as

T[] array;

, is guaranteed by the Class<T> parameter and the cast using the same type variable.

You should add the

@SuppressWarnings("unchecked")

with a comment explaining the above reason in your source code. Always comment why a cast whose warning you are suppressing is safe.

like image 80
Sotirios Delimanolis Avatar answered Dec 30 '22 12:12

Sotirios Delimanolis


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();

    // ...
}
like image 25
Radiodef Avatar answered Dec 30 '22 14:12

Radiodef