Java 如何创建通用数组?

Java 如何创建通用数组?,java,generics,Java,Generics,我不理解泛型和数组之间的联系 我可以使用泛型类型创建数组引用: private E[] elements; //GOOD elements = new E[10]; //ERROR 但无法创建泛型类型的数组对象: private E[] elements; //GOOD elements = new E[10]; //ERROR 但它是有效的: elements = (E[]) new Object[10]; //GOOD 下面是LinkedList#toArray(T[])的实现:

我不理解泛型和数组之间的联系

我可以使用泛型类型创建数组引用:

private E[] elements; //GOOD
elements = new E[10]; //ERROR
但无法创建泛型类型的数组对象:

private E[] elements; //GOOD
elements = new E[10]; //ERROR
但它是有效的:

elements = (E[]) new Object[10]; //GOOD

下面是
LinkedList#toArray(T[])
的实现:

public T[]toArray(T[]a){
如果(a.长度<尺寸)
a=(T[])java.lang.reflect.Array.newInstance(
a、 getClass().getComponentType(),大小);
int i=0;
对象[]结果=a;
for(节点x=first;x!=null;x=x.next)
结果[i++]=x项;
如果(a.长度>尺寸)
a[size]=null;
返回a;
}

简而言之,您只能通过
数组创建泛型数组。newInstance(Class,int)
其中
int
是数组的大小。

问题是,在运行时,新的E[10]相当于
新对象[10]

这将是危险的,因为可能会将
E
类型以外的数据放入数组。这就是为什么你需要明确地说出你想要的类型

  • 创建对象数组并将其强制转换为
    E[]
    array,或
  • 用于创建传入
    componentType
    argument的类型的数组的实际实例
已检查:

public Constructor(Class<E> c, int length) {

    elements = (E[]) Array.newInstance(c, length);
}

您不应该混淆数组和泛型。他们相处不好。数组和泛型类型执行类型检查的方式不同。我们说数组是具体化的,但泛型不是。因此,您可以在处理数组和泛型时看到这些差异

数组是协变的,泛型不是: 这意味着什么?您现在必须知道以下作业是有效的:

Object[] arr = new String[10];
基本上,
对象[]
字符串[]
的超级类型,因为
对象
字符串
的超级类型。对于泛型,情况并非如此。因此,以下声明无效,无法编译:

List<Object> list = new ArrayList<String>(); // Will not compile.
将很好地编译,但由于ArrayStoreCheck,将在运行时失败。对于泛型,这是不可能的,因为编译器将试图通过提供编译时检查来防止运行时异常,避免创建这样的引用,如上所示

那么,通用数组创建的问题是什么? 创建其组件类型为类型参数、具体参数化类型或有界通配符参数化类型的数组是不安全的

考虑以下代码:

public <T> T[] getArray(int size) {
    T[] arr = new T[size];  // Suppose this was allowed for the time being.
    return arr;
}
现在,假设您将此方法称为:

Integer[] arr = getArray(10);
问题出在这里。您刚刚将
对象[]
分配给了
整数[]
的引用。上面的代码可以很好地编译,但在运行时会失败

这就是禁止创建通用数组的原因

为什么将
新对象[10]
键入
E[]
有效? 现在是最后一个疑问,为什么下面的代码可以工作:

E[] elements = (E[]) new Object[10];
上述代码具有与上述相同的含义。如果您注意到,编译器将在那里给您一个未检查强制转换警告,因为您正在对未知组件类型的数组进行类型转换。这意味着,强制转换可能在运行时失败。例如,如果您在上述方法中有该代码:

public <T> T[] getArray(int size) {
    T[] arr = (T[])new Object[size];        
    return arr;
}
这将在运行时以ClassCastException失败。所以,不,这种方法不会一直奏效

创建类型为
List[]
的数组怎么样? 问题是一样的。由于类型擦除,一个
List[]
只不过是一个
List[]
。因此,如果允许创建这样的阵列,让我们看看会发生什么:

List<String>[] strlistarr = new List<String>[10];  // Won't compile. but just consider it
Object[] objarr = strlistarr;    // this will be fine
objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.
上述两种情况都可以,因为
List
是泛型类型
List
的所有实例化的超级类型。因此,它不会在运行时发出ArrayStoreException。这种情况与原始类型数组相同。由于原始类型也是可重新定义的类型,您可以创建一个数组
List[]

所以,它就像,你只能创建一个数组的可偿还类型,但不能不可偿还类型。注意,在上述所有情况下,数组的声明都很好,使用
new
操作符创建数组会产生问题。但是,声明这些引用类型的数组没有意义,因为它们只能指向
null
(忽略无界类型)

对于
E[]
,是否有解决方法? 可以,您可以使用以下方法创建阵列:

public E[]getArray(类clazz,int-size){
@抑制警告(“未选中”)
E[]arr=(E[])Array.newInstance(clazz,size);
返回arr;
}

Typecast是必需的,因为该方法返回一个
对象
。但你可以肯定这是一个安全的演员阵容。因此,您甚至可以对该变量使用@SuppressWarnings。

只需使用
new ArrayList()
?但是(E[])不会通过类型erasure转换为(O[])。@user2693979它会。泛型是编译器工具,而不是运行时。我不太明白你想展示什么问题……USER 26939 79如果你认为更好的话,你应该接受Rohit的回答。并没有压力,只是因为我之前发布了我的。但若E[]将是Object[],而(E[])将是Object[],那个么(E=newe[10])和(E=(E[])Object[10]之间的区别是什么呢?不是都是e=new Object[10]?@user2693979我怀疑
new e[size]
不允许阻止人们认为我们实际上是在创建
e
类型的数组,而不是
Object
类型的数组。我在回答中提到的方法清楚地表明了正在发生的事情,而
newe[size]
可能会被错误地解释。但同样,这只是我的怀疑。吹毛求疵:“创建组件类型为…通配符参数化类型的数组是类型不安全的。”实际实例化例如
newlist[]{}
是有效的-只是通配符不能被绑定。此外,“这将在运行时失败,并带有ClassCastException。”也不完全正确。如果取消选中强制转换,则意味着它不会很快失败。相反,ClassCastException
public <T> T[] getArray(int size) {
    T[] arr = (T[])new Object[size];        
    return arr;
}
String[] arr = getArray(10);
List<String>[] strlistarr = new List<String>[10];  // Won't compile. but just consider it
Object[] objarr = strlistarr;    // this will be fine
objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.
List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>();  // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine
public <E> E[] getArray(Class<E> clazz, int size) {
    @SuppressWarnings("unchecked")
    E[] arr = (E[]) Array.newInstance(clazz, size);

    return arr;
}