如何在java中创建一个类型安全的通用数组?

时间:2014-01-30 21:02:49

标签: java arrays generics

我想在java中创建一个通用数组,保持Java通常提供的类型安全性。

我正在使用此代码:

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

此代码类型是否安全?如果是这样,为什么?为什么如果它是类型安全我需要演员?

4 个答案:

答案 0 :(得分:9)

Array.newInstance(..)方法的返回类型为Object。因此,您无法直接将其分配给Object以外的任何其他内容。因此你需要一个演员。

该方法委托给native方法

  

创建具有指定组件类型和长度的新数组

因此,它正在创建一个T类型的数组。

类型安全,假设array被声明为

T[] array;

,由Class<T>参数和使用相同类型变量的强制转换保证。

您应该添加

@SuppressWarnings("unchecked")

在您的源代码中解释上述原因的评论。总是评论为什么你正在压制警告的演员是安全的。

答案 1 :(得分:2)

由于原始的Class对象,它不是类型安全的。例如,我可以通过以下方式创建一个新的堆栈:

new Stack<Boolean>(boolean.class, 10);

编译器可以,但会抛出异常,因为boolean.classClass<Boolean>boolean[]无法转换为Boolean[]

您展示的替代方案已注释掉:

array = (T[])new Object[size];

实际上有点类型安全,但出于不同的原因:它是由于擦除。例如,您不能将new Object[size]强制转换为Number[],但是阵营中永远不会发生转换。它会在一段时间后发生,就像从方法中返回数组元素一样(在这种情况下元素被转换)。如果您尝试执行某些操作(例如将数组返回到对象外部),则会引发异常。

通常解决方案不是一般地键入数组。相反,做这样的事情:

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;
    }
}

以上演员类型是安全的,因为您只能将E推入数组。 JDK Stack(扩展Vector)和ArrayList都以这种方式工作。

如果你想使用newInstance,你必须拒绝原语,因为无法一般地表示它们:

Stack(Class<T> tClass, int size) {
    if(tClass.isPrimitive())
        throw new IllegalArgumentException();

    // ...
}

答案 2 :(得分:1)

由于Array.newInstance返回Object,因此需要进行投射。在这种情况下,编译器将始终发出警告。这是泛型的限制。

答案 3 :(得分:0)

我们知道在编译时检查泛型,我的朋友 java.lang.reflect.Array.newInstance(tClass,大小);返回对象并输入类型,如果数组不是T []类型,则可能存在编译时错误