我们可以创建一个通用类数组吗?

时间:2011-09-01 19:54:53

标签: java generics type-erasure

  

可能重复:
  Java how to: Generic Array creation

我想创建一个包含10个元素的Stack,我正在尝试使用Generics。 我首先创建了一个Type T的Stack类,但是当我尝试实例化泛型类型的数组时,我遇到了编译错误。

public class Stack<T>{
    private T[] stk = new T[10];
}

我在这里做错了什么?

3 个答案:

答案 0 :(得分:3)

你不能这样做。并非没有使用一些关于代码的hacky回合,即使这样你最终还是要进行unsafe强制转换,这完全破坏了首先尝试保证类型安全的全部原因。

这是因为Java有一个名为type erasure的东西,编译器抛弃所有通用信息,而运行时不知道T是什么。

以下是执行此操作的首选方式:

@SuppressWarnings("unchecked")
static <T> T[] newArray(Class<T> type, int length)
{
    return (T[]) java.lang.reflect.Array.newInstance(type, length);
}

这不会在toArray()实施hack上调用List<T>的天真解决方案中创建关于列表实例的抛出。

有关此内容的更多讨论,请参阅

Here is an answer to a similar question about creating a type safe array at runtime

答案 1 :(得分:1)

有理由你无法创建泛型类型的数组:

当实例化泛型类型时,编译器通过称为类型擦除的技术转换这些类型 - 这是一种编译器删除与类型参数相关的所有信息并在类或方法中键入参数的过程。类型擦除使得使用泛型的Java应用程序能够维护与泛型之前创建的Java库和应用程序的二进制兼容性。

例如,Box被转换为Box类型,它被称为原始类型 - 原始类型是没有任何类型参数的泛型类或接口名称。这意味着您无法找到泛型类在运行时使用的Object类型。以下操作是不可能的:

  public class MyClass<E> {
    public static void myMethod(Object item) {
        if (item instanceof E) {  //Compiler error
            ...
        }
        E item2 = new E();       //Compiler error
        E[] iArray = new E[10];  //Compiler error
        E obj = (E)new Object(); //Unchecked cast warning
    }
  }

http://download.oracle.com/javase/tutorial/java/generics/erasure.html

使用泛型的原因是你不想进行类型转换。

那就是说,这是两个解决方案。

你可以使用一个对象数组,并保证你只能通过你提供的方法来推送和弹出T来访问数组:

public class MyStack2<T>  {
Object[] myStack2;

public MyStack2() {
    myStack2 = new Object[10];
}

public void push(T t) {
    // do whatever you wanna do to push t, like myStack2[x] = t;

}

public T pop() {
    // do whatever you wanna do to pop t like return (T)myStack2[0];
            // Here we typecasted, but we can be sure that myStack2 contains only T's
            // because we garanteed it at the push method.
}

}

OR

您可以使用除数组之外的其他内容来存储堆栈。

    public class MyStack<T> {
    Stack<T> myStack;

    public MyStack() {
        myStack = new Stack<T>();
    }

    public void push(T t) {
        myStack.push(t);
    }

    public T pop() {
        return myStack.pop();
    }
}

正如你所看到的,java已经提供了一个Stack类,所以你不必编写一个,但如果你真的想这样做,也许要了解它是如何工作的,你可以在这个例子中替换Stack类。一个列表。使用List你可以玩几乎和你使用阵列时一样的游戏。

答案 2 :(得分:1)

这不起作用的原因是当您在Java中编写类似String[]的类型时,这意味着该对象在运行时知道其组件类型为String(或子类)物)。通用类型参数在运行时不可用,因此您无法执行此操作。我们希望有类似Object[]<T>的东西,即“在运行时不知道其组件类型但在编译时具有泛型类型检查的数组”,但语言中不存在这样的事情。

但是,似乎从您的代码中打算在内部使用此数组,并且您并不关心数组是否在运行时知道T;你只需要一个数组,句号。如果您不打算将数组传递出类,那么有两个选项:

  1. 使用Object[]类型的变量,并创建new Object[10]。如果你想从一个方法中返回一个元素,这需要你强制转换为T

  2. 使用T[]类型的变量,并使用= (T[])new Object[10]分配给它。现在人们会指出子类型关系在技术上并不正确,但只要它在类中(在T范围内),就没关系,因为T被删除了。所以使用这种方法你必须格外小心,不要传递或返回数组引用,因为你不会被警告它不安全。

  3. 两种方法在类型擦除后都是相同的,并且这两种方法都会导致未经检查的强制转换警告,因此这实际上是您的个人偏好。第二个更方便(你可以假装它是一个T[]并且没有演员就把它拿出来),而第一个更正式更正确和更安全(第二个要求你努力不要把它传出去)。