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

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

问题描述

我不明白泛型和数组之间的联系.

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

但它有效:

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.

这是什么意思?您现在必须知道以下分配是有效的:

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

Object[] arr = new String[10];

基本上,Object[]String[] 的超类型,因为 ObjectString 的超类型.泛型不是这样.因此,以下声明无效,无法编译:

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.

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

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);

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

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.

创建其组件类型为类型参数具体参数化类型有界通配符参数化类型的数组是类型不安全.

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

考虑如下代码:

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

由于T的类型在运行时是未知的,所以创建的数组实际上是一个Object[].所以上面的方法在运行时看起来像:

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);

问题来了.您刚刚为 Integer[] 的引用分配了一个 Object[].上面的代码可以正常编译,但在运行时会失败.

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.

现在你的最后一个疑问,为什么下面的代码有效:

Now your last doubt, why the below code works:

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

上面的代码具有与上面解释相同的含义.如果您注意到,编译器会在那里给您一个 Unchecked Cast Warning,因为您正在对未知组件类型的数组进行类型转换.这意味着,转换可能会在运行时失败.例如,如果您在上述方法中有该代码:

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;
}

然后你像这样调用它:

String[] arr = getArray(10);

这将在运行时失败并出现 ClassCastException.所以,不,这种方式并不总是有效.

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

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

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.

现在上述情况下的 ArrayStoreCheck 将在运行时成功,尽管这应该抛出 ArrayStoreException.这是因为 List[]List[] 在运行时都被编译为 List[].

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.

是的.原因是,List 是可具体化的类型.这是有道理的,因为根本没有关联的类型.因此,不会因为类型擦除而丢失任何内容.因此,创建这种类型的数组是完全类型安全的.

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

以上两种情况都可以,因为List 是泛型类型List 的所有实例化的超类型.因此,它不会在运行时发出 ArrayStoreException.原始类型数组的情况相同.由于原始类型也是可具体化的类型,您可以创建一个数组 List[].

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[].

因此,它就像,您只能创建一个可具体化的类型数组,而不能创建不可具体化的类型.请注意,在上述所有情况下,数组声明都可以,这是使用 new 运算符创建数组,这会产生问题.但是,声明这些引用类型的数组是没有意义的,因为它们只能指向 null(忽略无界类型).

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).

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

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;
}

需要类型转换,因为该方法返回一个 Object.但是你可以确定这是一个安全的演员.因此,您甚至可以在该变量上使用 @SuppressWarnings.

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天全站免登陆