4

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?

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
Giuseppe Pes
  • 7,772
  • 3
  • 52
  • 90
  • In the above code `(T[])` simply casts to `Object[]`, since the type T is unknown at compile time. Therefore `array` must be of type `Object[]` (or simply `Object`). If you *return* a value typed `T[]` then, at the point of call, the compiler will throw in a hidden cast to the appropriate type, when the method's return value is assigned or used. – Hot Licks Jan 30 '14 at 21:17
  • Can you update your code so it compiles? Where does `T` come from? How is `array` declared? For example is this a static method you have mind (such that is just a wrapper around `Array#newInstance`) or is `array` a field and `T` is a type parameter to a class? – Radiodef Jan 30 '14 at 21:32

4 Answers4

9

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.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • why I am getting an unchecked warning? – Giuseppe Pes Jan 30 '14 at 21:12
  • 1
    You are making an *unchecked cast* so you get a warning for using it. – Marko Topolnik Jan 30 '14 at 21:13
  • @GiuseppePes Because the compiler can't guarantee it. The method specification should. – Sotirios Delimanolis Jan 30 '14 at 21:13
  • 1
    Note that the compiler could have guaranteed it if there was a `newInstance` specifically dedicated to reference-typed arrays. `static T[] newInstance(Class c, int size)` – Marko Topolnik Jan 30 '14 at 21:14
  • So, I cannot get type safety guarantee by the compiler because I am using the generic. Nevertheless, the type safety is guarantee at run time by parameter passed in the constructor? – Giuseppe Pes Jan 30 '14 at 21:19
  • 5
    Note that doing this will throw an exception for primitive types. For example `int.class` is a `Class` therefore casting an `int[]` into an `Integer[]`. There's not a way to represent the primitives generically. That's why it could be unsafe. – Radiodef Jan 30 '14 at 21:19
  • @GiuseppePes Guaranteed for everything except Radiodef's comment above mine. You wouldn't typically be using primitives with generics though. – Sotirios Delimanolis Jan 30 '14 at 21:23
  • @Radiodef brought up a really good point. If I want to create a utterly generic container able to contain any type including primitive types I have to take into account this problem . Wouldn't be better to use a `T[] array = (T[])Object[size];` – Giuseppe Pes Jan 30 '14 at 21:34
  • @GiuseppePes Basically you can't include primitives if you want to do this generically in the way you're doing. – Radiodef Jan 30 '14 at 21:39
4

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

    // ...
}
Radiodef
  • 37,180
  • 14
  • 90
  • 125
1

Since Array.newInstance returns an Object it needs a cast. The compiler will always give warning in such cases. This is the limitation of generics.

fastcodejava
  • 39,895
  • 28
  • 133
  • 186
0

As we know that generics are checked at the compile time so, my friend java.lang.reflect.Array.newInstance(tClass,size); returns you the object and you are type casting it, and if array is not of type T[] then there can be compile time error

Girish
  • 1,717
  • 1
  • 18
  • 30