ArrayList.toArray() 中的 Java 泛型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36598928/
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-11-03 01:39:07  来源:igfitidea点击:

Java generics in ArrayList.toArray()

javaarraysgenerics

提问by Rabbit Guy

Say you have an arraylist defined as follows:

假设您有一个定义如下的数组列表:

ArrayList<String> someData = new ArrayList<>();

Later on in your code, because of generics you can say this:

稍后在您的代码中,由于泛型,您可以这样说:

String someLine = someData.get(0);

And the compiler knows outright that it will be getting a string. Yay generics! However, this will fail:

并且编译器完全知道它将获得一个字符串。耶泛型!但是,这将失败:

String[] arrayOfData = someData.toArray();

toArray()will always return an array of Objects, not of the generic that was defined. Why does the get(x)method know what it is returning, but toArray()defaults to Objects?

toArray()将始终返回一个对象数组,而不是定义的泛型。为什么get(x)方法知道它返回的是什么,但toArray()默认为对象?

采纳答案by justAbit

If you look at the implementation of toArray(T[] a)of ArrayList<E>class, it is like:

如果你看一下执行toArray(T[] a)的ArrayList <E>类,它是这样的:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

Problem with this method is that you need to pass array of the same generic type. Now consider if this method do not take any argument then the implementation would be something similar to:

此方法的问题在于您需要传递相同泛型类型的数组。现在考虑如果这个方法不接受任何参数,那么实现将类似于:

public <T> T[] toArray() {
    T[] t = new T[size]; // compilation error
    return Arrays.copyOf(elementData, size, t.getClass());
}

But the problem here is that you can not create generic arrays in Javabecause compiler does not know exactly what Trepresents. In other words creation of array of a non-reifiable type(JLS §4.7) is not allowed in Java.

但这里的问题是你不能在 Java 中创建泛型数组,因为编译器不知道到底T代表什么。换句话说,Java 中不允许创建不可具体化类型JLS §4.7的数组

Another important quote from Array Store Exception(JLS §10.5):

Array Store Exception 的另一个重要引述(JLS §10.5):

If the component type of an array were not reifiable (§4.7), the Java Virtual Machine could not perform the store check described in the preceding paragraph. This is why an array creation expression with a non-reifiable element type is forbidden (§15.10.1).

如果数组的组件类型不可具体化(第 4.7 节),则 Java 虚拟机无法执行上一段中描述的存储检查。这就是禁止具有不可具体化元素类型的数组创建表达式的原因(第 15.10.1 节)。

That is why Java has provided overloaded version toArray(T[] a).

这就是 Java 提供重载版本的原因toArray(T[] a)

I will override the toArray() method to tell it that it will return an array of E.

我将覆盖 toArray() 方法以告诉它它将返回一个 E 数组。

So instead of overriding toArray(), you should use toArray(T[] a).

因此toArray(),您应该使用toArray(T[] a).

Cannot Create Instances of Type Parametersfrom Java Doc might also be interesting for you.

无法从 Java Doc创建类型参数的实例对您来说也可能很有趣。

回答by AdamSkywalker

Generic information is erasedat runtime. JVM does not know whether your list is List<String>or List<Integer>(at runtime Tin List<T>is resolved as Object), so the only possible array type is Object[].

通用信息在运行时被擦除。JVM 不知道您的列表是否为List<String>List<Integer>(在运行时TinList<T>解析为Object),因此唯一可能的数组类型是Object[]

You can use toArray(T[] array)though - in this case JVM can use the class of a given array, you can see it in the ArrayListimplementation:

您可以使用toArray(T[] array)- 在这种情况下,JVM 可以使用给定数组的类,您可以在ArrayList实现中看到它:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());

回答by ach

If you look at the Javadoc for the Listinterface, you'll notice a second form of toArray: <T> T[] toArray(T[] a).

如果你看一下Javadoc文档的List界面,你会发现的第二种形式toArray<T> T[] toArray(T[] a)

In fact, the Javadoc even gives an example of how to do exactly what you want to do:

事实上,Javadoc 甚至给出了一个例子,说明如何做你想做的事情:

String[] y = x.toArray(new String[0]);

String[] y = x.toArray(new String[0]);

回答by senseiwu

I can, and will use an iterator instead of making an array sometimes, but this just always seemed strange to me. Why does the get(x) method know what it is returning, but toArray() defaults to Objects? Its like half way into designing it they decided this wasn't needed here??

我可以,并且有时会使用迭代器而不是创建数组,但这对我来说总是很奇怪。为什么 get(x) 方法知道它返回的是什么,而 toArray() 默认为 Objects?就像设计到一半他们决定这里不需要这个??

As the intention of the question seems to be not just about getting around using toArray()with generics, rather also about understanding the design of the methods in the ArrayListclass, I would like to add:

由于这个问题的意图似乎不仅仅是关于使用toArray()泛型,而是关于理解ArrayList类中方法的设计,我想补充:

ArrayListis a generic class as it is declared like

ArrayList是一个泛型类,因为它被声明为

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

which makes it possible to use Generic methods such as public E get(int index)within the class.

这使得public E get(int index)在类中使用泛型方法成为可能。

But if a method such as toArray()is not returning E, rather E[]then things start getting a bit tricky. It would not be possible to offer a signature such as public <E> E[] toArray()because it is not possible to create generic arrays.

但是如果一个方法toArray()没有返回EE[]那么事情就会开始变得有点棘手。public <E> E[] toArray()由于无法创建通用数组,因此无法提供签名。

Creation of arrays happen at runtime and due to Type erasure, Java runtime has no specific information of the type represented by E. The only workaround as of now is to pass the required type as a parameter to the method and hence the signature public <T> T[] toArray(T[] a)where clients are forced to pass the required type.

数组的创建发生在运行时,由于类型擦除,Java 运行时没有由 表示的类型的特定信息E。目前唯一的解决方法是将所需的类型作为参数传递给方法,从而public <T> T[] toArray(T[] a)强制客户端传递所需类型的签名。

But on the other hand, it works for public E get(int index)because if you look at the implementation of the method, you would find that even though the method makes use of the same array of Object to return the element at the specified index, it is casted to E

但另一方面,它是有效的,public E get(int index)因为如果您查看该方法的实现,您会发现即使该方法使用相同的 Object 数组返回指定索引处的元素,它也被强制转换为E

E elementData(int index) {
    return (E) elementData[index];
}

It is the Java compiler which at the compile time replaces Ewith Object

它是Java编译器这在编译时替换EObject

回答by newacct

The pertinent thing to note is that arrays in Java know their component type at runtime. String[]and Integer[]are different classes at runtime, and you can ask arrays for their component type at runtime. Therefore, a component type is needed at runtime (either by hard-coding a reifiable component type at compile time with new String[...], or using Array.newInstance()and passing a class object) to create an array.

需要注意的相关事项是 Java 中的数组在运行时知道它们的组件类型。String[]Integer[]在运行时是不同的类,您可以在运行时向数组询问它们的组件类型。因此,在运行时需要一个组件类型(通过在编译时硬编码一个可具体化的组件类型new String[...],或者使用Array.newInstance()和传递一个类对象)来创建一个数组。

On the other hand, type arguments in generics do not exist at runtime. There is absolutely no difference at runtime between an ArrayList<String>and a ArrayList<Integer>. It is all just ArrayList.

另一方面,泛型中的类型参数在运行时不存在。anArrayList<String>和 a之间在运行时绝对没有区别ArrayList<Integer>。这一切都只是ArrayList

That's the fundamental reason why you can't just take a List<String>and get a String[]without passing in the component type separately somehow -- you would have to get component type information out of something that doesn't have component type information. Clearly, this is impossible.

这就是为什么您不能在不以某种方式分别传入组件类型List<String>String[]情况下只获取 a和 get a的根本原因——您必须从没有组件类型信息的东西中获取组件类型信息。显然,这是不可能的。

回答by Tony

The very first thing you have to understand is what ArrayListown is just an array of Object

你首先要明白的是,ArrayList拥有的只是一个数组Object

   transient Object[] elementData;

When it comes to the reason why T[]is fail, it because you can't get an array of generic type without a Class<T>and this is because java's type erase( there is a more explanationand how to create one). And the array[]on the heap knows its type dynamically and you can't cast int[]to String[]. The same reason, you can't cast Object[]to T[].

说到T[]失败的原因,是因为没有 a 就无法获得泛型类型的数组,Class<T>这是因为 java 的类型擦除(有更多解释以及如何创建一个)。而array[]在堆上知道它的类型动态,你可以不投int[]String[]。同样的原因,你不能投射Object[]T[].

   int[] ints = new int[3];
   String[] strings = (String[]) ints;//java: incompatible types: int[] cannot be converted to java.lang.String[]

   public <T> T[] a() {
      Object[] objects = new Object[3];
      return (T[])objects;
   }
   //ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
   Integer[] a = new LearnArray().<Integer>a();

But what you put into the arrayis just a object which type is E(which is checked by compiler), so you can just cast it to Ewhich is safe and correct.

但是您放入的array只是一个类型E(由编译器检查)的对象,因此您可以将其强制转换E为安全且正确的对象。

  return (E) elementData[index];

In short, you can't get what don't have by cast. You have just Object[], so toArray()can just return Object[](otherwise, you have to give it a Class<T>to make a new array with this type). You put Ein ArrayList<E>, you can get a Ewith get().

简而言之,您无法通过演员表获得没有的东西。你刚刚Object[],所以toArray()可以返回Object[](否则,你必须给它 aClass<T>用这种类型创建一个新数组)。你把EArrayList<E>,你可以得到一个Eget()

回答by Gerald Mücke

An array is of a different type than the type of the array. It's sort of StringArray class instead of String class.

数组的类型与数组的类型不同。它有点像 StringArray 类而不是 String 类。

Assuming, it would be possible, an Generic method toArray()would look like

假设有可能,泛型方法toArray()看起来像

private <T> T[] toArray() {
    T[] result = new T[length];
    //populate
    return result;
}

Now during compilation, the type T gets erased. How should the part new T[length]be replaced? The generic type information is not available.

现在在编译期间,类型 T 被擦除。应该如何new T[length]更换零件?通用类型信息不可用。

If you look at the source code of (for example) ArrayList, you see the same. The toArray(T[] a)method either fills the given array (if the size matches) or creates a new new array using the typeof the parameter, which is the array-type of the Generic Type T.

如果您查看 (for example) 的源代码ArrayList,您会看到相同的内容。该toArray(T[] a)方法要么填充给定的数组(如果大小匹配),要么使用参数的类型创建一个新数组,该类型是泛型类型 T 的数组类型。

回答by Alexander Samoylov

It is possible to create a "generic" array of the given(known) type. Normally I use something like this in my code.

可以创建给定(已知)类型的“通用”数组。通常我在我的代码中使用这样的东西。

public static <T> T[] toArray(Class<T> type, ArrayList<T> arrList) {
    if ((arrList == null) || (arrList.size() == 0)) return null;
    Object arr = Array.newInstance(type, arrList.size());
    for (int i=0; i < arrList.size(); i++) Array.set(arr, i, arrList.get(i));
    return (T[])arr;
}