我想使用一个数组实现两个堆栈。我还希望堆栈实现是通用的。这就是我所做的:
public class TwoStack<T>
{
private T array[];
public TwoStack(int size)
{
array = new T[size];
}
//additional push/pop implementation
}
但是编译器会在构造函数内部的行中抛出异常,说“无法创建泛型数组。
”我知道由于类型擦除,我无法创建T类型的新对象。但为什么我不能创建对类型为T的对象的引用?
答案 0 :(得分:3)
Java数组实际上在运行时知道它的组件类型。如您所知,泛型类(如TwoStack<T>
)在运行时对其泛型参数一无所知。这就是为什么会发生冲突的原因。 T[]
表示在运行时知道其组件类型T
(或T
的子类)的类。因此,您需要在运行时知道T
来创建它。这不是你想要的。
有些人提到使用ArrayList<T>
等,而不是数组。这是为什么?因为ArrayList<T>
与TwoStack<T>
一样,在运行时不知道或不关心T
,所以它是一个完美的匹配。
仍然可以使用数组吗?当然。 (ArrayList
是使用数组实现的,所以任何可以用ArrayList
完成的事情都可以用数组来完成。)但是正如我们之前建立的那样,T[]
不是你想要的。
我们想要一些在运行时不关心T
的东西。这是Object[]
。所以我们必须在运行时创建Object[]
。遗憾的是,Java中没有Object[]<T>
,因此您将失去泛型的编译时优势。很多人为数组创建了通用的包装类,类似于Array<T>
;您也可以轻松地创建一个,作为练习,但它不存在于Java库中。
如果要使用数组,有两个选项:
只需使用Object[]
,然后根据需要投射到您想要的元素,就像在泛型之前使用集合一样。你失去了仿制药的所有细节,但是你完全诚实地在板上工作。
谎言,欺骗并将Object[]
投射到T[]
(即(T[])new Object[size]
)。现在,这表面看起来就像一个非法演员。 (如果您,例如创建Object[]
并将其投放到String[]
,则会立即获得ClassCastException
,因为Object[]
不是String[]
的子类,虽然它是另一种方式。)但请记住,只要你在泛型类中,T
被删除到最低界限,即Object
,所以Object[]
到Object[]
演员阵容没问题。危险将推迟到以后的时间:如果您将此数组作为类型T[]
返回,或者允许外部源获取对此数组的引用,并且您向它们承诺它是T[]
,并且那些人期望它是T[]
的实际类型(例如String[]
),编译器会插入一个演员,你将得到一个ClassCastException
。但是,如果你注意不要将这个数组暴露给外面的世界,那么没有人会知道T[]
这个谎言,你可以在它上面使用类型安全的操作而不会转换为T
答案 1 :(得分:1)
答案 2 :(得分:1)
这是因为在运行时,没有关于您在编译时使用的泛型类型的信息。因此,运行时无法知道您是想要Object[]
还是String[]
。您只需创建Object[]
并稍后将其投放到T[]
。
答案 3 :(得分:0)
Java不允许构造没有反射或弱转换的通用数组。
答案 4 :(得分:0)
使用ArrayList而不是[]数组。它只是一点点慢,它可以用于此目的,而Java禁止你想要做的事情。
答案 5 :(得分:0)
Array是穷人的仿制品。
如果只允许Object[]
,那么Java就可以了。如果你只限于Object[]
,你就可以得到很好的结果。但就像非通用的List
一样,使用它有点痛苦。程序员需要一些其他方法来提醒自己数组存储的对象类型。
因此,Java允许您指定数组中元素的类型,如Number[]
,类似于generified List<Number>
。现在javac可以确保你没有插入错误类型的元素;而且你不必向下转换从数组中检索的元素;并且有一个简单的协方差(例如,Interger[]
是Number[]
的子类型)
因此,在引入真正的泛型之前,数组在API中优于List
。最好返回Number[]
而不是原始List
;后者很难用于来电者。
现在, 真正的仿制药,这些文学的好处不再是相关的。几乎在实施的任何地方,您需要X[]
,您可以使用ArrayList<X>
代替;在AP的任何地方,您需要X[]
,您可以使用List<X>
代替。
当您确实需要使用数组时,例如编程ArrayList<T>
本身或其他一些基本数据结构时,可以仅使用Object[]
。通用数组对你来说不会有太多帮助,同时它本身也很混乱。