Java 如何创建通用数组?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18581002/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 09:12:02  来源:igfitidea点击:

How to create a generic array?

javagenerics

提问by user2693979

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

采纳答案by Rohit Jain

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 Objectis a super type of String. This is not true with generics. So, the following declaration is not valid, and won't compile:

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

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.

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

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

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

那么,通用数组创建有什么问题呢?

Creation of array whose component type is either a type parameter, a concrete parameterized typeor 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 Tis not known at runtime, the array created is actually an Object[]. So the above method at runtime will look like:

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

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.

这就是问题所在。您刚刚将 分配Object[]给了 的引用Integer[]。上面的代码可以很好地编译,但在运行时会失败。

That is why generic array creation is forbidden.

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

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

为什么类型转换new Object[10]E[]作品?

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 Warningthere, 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.

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

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

创建一个类型的数组怎么样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:

问题是一样的。由于类型擦除, aList<String>[]只不过是 a 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.

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.

现在,上述情况下的 ArrayStoreCheck 将在运行时成功,尽管应该抛出 ArrayStoreException。那是因为List<String>[]List<Integer>[]都是List[]在运行时编译的。

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.

是的。原因是, aList<?>是可具体化的类型。这是有道理的,因为根本没有关联的类型。因此,不会因为类型擦除而丢失任何东西。因此,创建此类类型的数组是完全类型安全的。

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

以上两种情况都很好,因为List<?>是泛型类型的所有实例化的超类型List<E>。因此,它不会在运行时发出 ArrayStoreException。这种情况与原始类型数组相同。由于原始类型也是可具体化的类型,您可以创建一个数组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 newoperator, 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).

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

Is there any workaround for E[]?

有什么解决方法E[]吗?

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

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

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.

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

回答by Josh M

Here is the implementation of LinkedList<T>#toArray(T[]):

下面是实现LinkedList<T>#toArray(T[])

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        a = (T[])java.lang.reflect.Array.newInstance(
                            a.getClass().getComponentType(), size);
    int i = 0;
    Object[] result = a;
    for (Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;

    if (a.length > size)
        a[size] = null;

    return a;
}

In short, you could only create generic arrays through Array.newInstance(Class, int)where intis the size of the array.

总之,你只能通过建立通用的阵列Array.newInstance(Class, int),其中int是数组的大小。

回答by Pshemo

Problem is that while runtime generic type is erasedso new E[10]would be equivalent to new Object[10].

问题是,虽然运行时泛型类型被删除,所以new E[10]将等同于new Object[10].

This would be dangerous because it would be possible to put in array other data than of Etype. That is why you need to explicitly say that type you want by either

这将是危险的,因为可以将其他数据放入数组而不是E类型。这就是为什么您需要通过以下任一方式明确说出您想要的类型

回答by Melih Alt?nta?

checked :

检查:

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

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

or unchecked :

或未选中:

public Constructor(int s) {
    elements = new Object[s];
}