如何创建一个通用数组? [英] How to create a generic array?

查看:90
本文介绍了如何创建一个通用数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白泛型和数组之间的关系。

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

  private E [] elements; // GOOD 

但是不能用泛型创建数组对象:

  elements = new E [10]; //错误

但它有效:

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


解决方案

你不应该混淆数组和仿制药。他们不一起很好。数组和泛型类型如何强制执行类型检查存在差异。我们说数组是通用的,但泛型不是。因此,你会发现这些区别与数组和泛型有关。



数组是协变的,泛型不是:

这意味着什么?您现在必须知道下列赋值是有效的:

  Object [] arr = new String [10]; 

基本上,一个 Object [] 是一个超类型的 String [] ,因为 Object 是超类型的 String 。泛型不适用这种情况。所以,下面的声明是无效的,并且不会被编译:

  List< Object> list = new ArrayList< String>(); //不会编译。 

原因在于,泛型是不变的。

强制类型检查:



在Java中引入泛型以在编译时强制执行类型检查。因此,由于类型擦除,泛型类型在运行时没有任何类型信息。 。因此, List< String> 的静态类型为 List< String> ,但动态类型 List



但是,数组带有组件类型的运行时类型信息。在运行时,数组使用Array Store检查来检查您是否插入与实际数组类型兼容的元素。所以,下面的代码:

  Object [] arr = new String [10]; 
arr [0] = new Integer(10);

会很好的编译,但是在运行时会失败,这是ArrayStoreCheck的结果。对于泛型,这是不可能的,因为编译器会尝试通过提供编译时检查来防止运行时异常,避免创建像上面这样的引用。



那么,通用数组创建的问题是什么?



创建其组件类型为类型参数具体参数化类型有界通配符参数化类型 type-unsafe

请考虑以下代码:

  public< T> T [] getArray(int size){
T [] arr = new T [size]; //假设这是暂时允许的。
return arr;





$ b $ T 在运行时是未知的,所创建的数组实际上是一个 Object [] 。因此,上面的方法在运行时会看起来像:

  public Object [] getArray(int size){
Object [ ] arr = new Object [size];
return arr;

$ / code>

现在,假设您将此方法称为:

  Integer [] arr = getArray(10); 

问题出在这里。您刚刚为整数[] 指定了一个 Object [] >。上面的代码可以很好的编译,但是在运行时会失败。



这就是为什么通用数组的创建被禁止的原因。

< h2>为什么输入新对象[10] E [] 有效?

现在您最后一个疑问,为什么下面的代码可以工作:

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

以上代码与上面解释的含义相同。如果您注意到,编译器会在那里给您一个 Unchecked Cast Warning ,因为您正在类型化为未知组件类型的数组。这意味着,演员阵容可能在运行时失败。例如,如果您在上述方法中使用了该代码:

  public< T> T [] getArray(int size){
T [] arr =(T [])new Object [size];
return arr;
}

你可以这样调用它:

  String [] arr = getArray(10); 

这会在运行时因ClassCastException失败。所以,不会总是这样。



创建类型的数组怎么样> List< String> []



问题是一样的。由于类型擦除, List< String> [] 不过是 List [] 。所以,如果允许创建这样的数组,我们来看看会发生什么:

  List< String> [] strlistarr = new List< ;字符串> [10]; //不会编译。但只是考虑它
Object [] objarr = strlistarr; //这会很好
objarr [0] = new ArrayList< Integer>(); //这会失败但成功。

现在,上面案例中的ArrayStoreCheck在运行时会成功,但应该抛出一个ArrayStoreException。这是因为 List< String> [] List< Integer> [] 被编译为在运行时列出[]

那么我们可以创建一个无界通配符参数化类型的数组吗?



是的。原因是 List<?> 是一个可定义的类型。这是有道理的,因为根本没有任何类型。所以类型删除的结果是没有什么可以松动的。因此,创建这种类型的数组是完全类型安全的。

  List<?> [] listArr = new列表与LT;?> [10]; 
listArr [0] = new ArrayList< String>(); //很好
listArr [1] = new ArrayList< Integer>(); // Fine

以上两种情况都没有问题,因为 List<?> ; 是泛型类型的所有实例的超类型 List< E> 。所以,它不会在运行时发出ArrayStoreException。这种情况与原始类型数组相同。由于原始类型也是可重用类型,因此您可以创建一个数组 List []



,您只能创建一个可修饰类型的数组,但不能创建不可修饰类型。请注意,在上述所有情况下,数组的声明没问题,它是使用 new 运算符创建数组的,这会产生问题。但是,声明这些引用类型的数组毫无意义,因为它们不能指向除忽略无限类型 )。

是否有任何解决方法: E []



是的,您可以使用 Array#newInstance() 方法:

  public< E> E [] getArray(Class< E> clazz,int size){
@SuppressWarnings(unchecked)
E [] arr =(E [])Array.newInstance(clazz,size);

return arr;



$ b $ p
$ b

因为该方法返回一个 Object 。但是你可以肯定这是一个安全的演员。因此,您甚至可以在该变量上使用@SuppressWarnings。


I don't understand the connection between generics and arrays.

I can create array reference with generic type:

private E[] elements; //GOOD

But can't create array object with generic type:

elements = new E[10]; //ERROR

But it works:

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

解决方案

You should not mix-up arrays and generics. They don't go well together. There are differences in how arrays and generic types enforce the type check. We say that arrays are reified, but generics are not. As a result of this, you see these differences working with arrays and generics.

Arrays are covariant, Generics are not:

What that means? You must be knowing by now that the following assignment is valid:

Object[] arr = new String[10];

Basically, an Object[] is a super type of String[], because Object is a super type of String. This is not true with generics. So, the following declaration is not valid, and won't compile:

List<Object> list = new ArrayList<String>(); // Will not compile.

Reason being, generics are invariant.

Enforcing Type Check:

Generics were introduced in Java to enforce stronger type check at compile time. As such, generic types don't have any type information at runtime due to type erasure. So, a List<String> has a static type of List<String> but a dynamic type of List.

However, arrays carry with them the runtime type information of the component type. At runtime, arrays use Array Store check to check whether you are inserting elements compatible with actual array type. So, the following code:

Object[] arr = new String[10];
arr[0] = new Integer(10);

will compile fine, but will fail at runtime, as a result of ArrayStoreCheck. With generics, this is not possible, as the compiler will try to prevent the runtime exception by providing compile time check, by avoiding creation of reference like this, as shown above.

So, what's the issue with Generic Array Creation?

Creation of array whose component type is either a type parameter, a concrete parameterized type or a bounded wildcard parameterized type, is type-unsafe.

Consider the code as below:

public <T> T[] getArray(int size) {
    T[] arr = new T[size];  // Suppose this was allowed for the time being.
    return arr;
}

Since the type of T is not known at runtime, the array created is actually an Object[]. So the above method at runtime will look like:

public Object[] getArray(int size) {
    Object[] arr = new Object[size];
    return arr;
}

Now, suppose you call this method as:

Integer[] arr = getArray(10);

Here's the problem. You have just assigned an Object[] to a reference of Integer[]. The above code will compile fine, but will fail at runtime.

That is why generic array creation is forbidden.

Why typecasting new Object[10] to E[] works?

Now your last doubt, why the below code works:

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

The above code have the same implications as explained above. If you notice, the compiler would be giving you an Unchecked Cast Warning there, as you are typecasting to an array of unknown component type. That means, the cast may fail at runtime. For e.g, if you have that code in the above method:

public <T> T[] getArray(int size) {
    T[] arr = (T[])new Object[size];        
    return arr;
}

and you call invoke it like this:

String[] arr = getArray(10);

this will fail at runtime with a ClassCastException. So, no this way will not work always.

What about creating an array of type List<String>[]?

The issue is the same. Due to type erasure, a List<String>[] is nothing but a List[]. So, had the creation of such arrays allowed, let's see what could happen:

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.

Now the ArrayStoreCheck in the above case will succeed at runtime although that should have thrown an ArrayStoreException. That's because both List<String>[] and List<Integer>[] are compiled to List[] at runtime.

So can we create array of unbounded wildcard parameterized types?

Yes. The reason being, a List<?> is a reifiable type. And that makes sense, as there is no type associated at all. So there is nothing to loose as a result of type erasure. So, it is perfectly type-safe to create an array of such type.

List<?>[] listArr = new List<?>[10];
listArr[0] = new ArrayList<String>();  // Fine.
listArr[1] = new ArrayList<Integer>(); // Fine

Both the above case is fine, because List<?> is super type of all the instantiation of the generic type List<E>. So, it won't issue an ArrayStoreException at runtime. The case is same with raw types array. As raw types are also reifiable types, you can create an array List[].

So, it goes like, you can only create an array of reifiable types, but not non-reifiable types. Note that, in all the above cases, declaration of array is fine, it's the creation of array with new operator, which gives issues. But, there is no point in declaring an array of those reference types, as they can't point to anything but null (Ignoring the unbounded types).

Is there any workaround for E[]?

Yes, you can create the array using Array#newInstance() method:

public <E> E[] getArray(Class<E> clazz, int size) {
    @SuppressWarnings("unchecked")
    E[] arr = (E[]) Array.newInstance(clazz, size);

    return arr;
}

Typecast is needed because that method returns an Object. But you can be sure that it's a safe cast. So, you can even use @SuppressWarnings on that variable.

这篇关于如何创建一个通用数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆