我正在尝试用Java编写一些简单的数字代码,以后可以在float和double之间进行选择。我班级的简化版本如下例所示:
public class UniformGrid<T> {
public T[] data;
public UniformGrid(int arrayDim) {
data = new T[arrayDim];
}
}
这不起作用我在尝试编译时遇到generic array creation
错误。谷歌搜索和阅读一些SO答案我学到了java.lang.reflect.Array
并试图使用
data = (T[]) Array.newInstance(T.class, arrayDim);
哪个也行不通,因为T(可能)是原始类型。我的Java知识非常生疏(特别是涉及泛型时),我想知道为什么new运算符不能与泛型数组类型一起使用。当然,我也对如何用Java解决这个问题感兴趣。
答案 0 :(得分:7)
由于type erasure,您无法在Java中创建通用数组。解决这个问题的最简单方法是使用List<T>
。但是,如果必须使用数组,则可以对数组使用Object[]
,并确保仅将T
个对象放入其中。 (这是ArrayList
采取的策略。)
例如:
private Object[] data = new Object[10];
private int size = 0;
public void add(T obj) {
data[size++] = obj;
}
public T get(int i){
return (T) data[i];
}
当然,你会从你的编译器那里得到一个未经检查的警告,但你可以抑制它。
答案 1 :(得分:1)
创建数组时不能使用泛型,因为您在运行时不知道T是什么类型。这称为type erasure。
解决方案很简单:使用List<T> data
。
答案 2 :(得分:1)
对不起,您必须采取另一种方法:
如果你真的需要这个,我会研究代码生成,也许是自动构建的一部分。 (对源代码的简单搜索和替换应该能够将在double上运行的库转换为在float上运行的库。)
答案 3 :(得分:1)
只要您使用Float
和Double
而不是float
和double
,这是可能的,因为Java Generics中不允许使用基本类型。当然,这可能会非常缓慢。并且,您将无法(安全地)允许直接公共访问阵列。所以这个答案不是很有用,但理论上可能很有趣。无论如何,如何构建数组...
data = (T[]) new Object[arrayDim];
这会给你一个警告,但这并不是什么值得担心的。它以这种特殊形式工作 - 它位于通用构造函数中,data
是对这个新构造对象的唯一引用。 See this page about this.
您将无法以您希望的方式公开访问此数组对象。您需要在UniformGrid<T>
中设置方法来获取和设置对象。这样,编译器将确保类型安全,运行时不会给您带来任何问题。
private T[] data;
public void set(int pos, T t) {
data[pos] = t;
}
public T get(int pos) {
return data[pos];
}
在这种情况下,set
的接口将(在编译时)强制执行正确的类型。底层数组的类型为Object[]
但是没关系,因为它可以采用任何引用类型 - 并且所有泛型类型在运行时都是有效的List<Object>
或类似的东西。
有趣的是吸气剂。编译器“知道”data
的类型是T[]
,因此getter将干净地编译并承诺返回T
。因此,只要您将data
设为私有,并且只能通过get
和set
访问它,那么一切都会好的。
一些示例代码位于ideone。
public static void main(String[] args) {
UniformGrid<A> uf = new UniformGrid<A>(1);
//uf.insert(0, new Object()); // compile error
uf.insert(0, new A());
uf.insert(0, new B());
Object o1= uf.get(0);
A o2= uf.get(0);
// B o2= uf.get(0); // compiler error
System.out.println(o1);
System.out.println(o2);
System.out.println("OK so far");
// A via_array1 = uf.data[0]; // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LA;
}
如您所愿,uf.insert(0, new Object())
和B o2= uf.get(0);
但是你不应该让data
成员公开。如果你这样做,你可以编写和编译A via_array1 = uf.data[0];
。该行看起来应该没问题,但是您得到了运行时异常:Ljava.lang.Object; cannot be cast to [LA;
。
简而言之,get
和set
接口提供了一个安全的界面。但是如果你使用数组这么麻烦,你应该只使用ArrayList<T>
。故事的道德:在任何语言(Java或C ++)中,使用泛型或没有泛型,只对数组说。 : - )
答案 4 :(得分:1)
Effective Java,2nd Edition 中的第25项讨论了这个问题:
阵列是协变的和具体化的;泛型是不变的和擦除的。 因此,数组提供运行时类型安全性,但不提供编译时类型安全性,反之亦然。一般而言,数组和泛型不能很好地混合。