Rab*_*Guy 58 java arrays generics
假设你有一个arraylist定义如下:
ArrayList<String> someData = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)
稍后在您的代码中,由于泛型,您可以这样说:
String someLine = someData.get(0);
Run Code Online (Sandbox Code Playgroud)
并且编译器完全知道它将获得一个字符串.是的仿制药!但是,这将失败:
String[] arrayOfData = someData.toArray();
Run Code Online (Sandbox Code Playgroud)
toArray()
将始终返回一个对象数组,而不是已定义的泛型数组.为什么该get(x)
方法知道它返回什么,但toArray()
默认为Objects?
sud*_*d29 57
如果你看一下执行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;
}
Run Code Online (Sandbox Code Playgroud)
此方法的问题是您需要传递相同泛型类型的数组.现在考虑这个方法是否不采用任何参数,那么实现将类似于:
public <T> T[] toArray() {
T[] t = new T[size]; // compilation error
return Arrays.copyOf(elementData, size, t.getClass());
}
Run Code Online (Sandbox Code Playgroud)
但问题是你不能用Java创建通用数组,因为编译器并不确切知道T
代表什么.换句话说,Java中不允许创建不可重类型的数组(JLS§4.7).
Array Store Exception的另一个重要引用(JLS§10.5):
如果数组的组件类型不可重新生成(第4.7节),则Java虚拟机无法执行前一段中描述的存储检查.这就是为什么禁止使用具有不可恢复元素类型的数组创建表达式(第15.10.1节).
这就是Java提供重载版本的原因toArray(T[] a)
.
我将覆盖toArray()方法,告诉它它将返回一个E数组.
因此toArray()
,您应该使用,而不是覆盖toArray(T[] a)
.
Ada*_*ker 20
通用信息在运行时被擦除.JVM不知道你的列表是否是List<String>
或List<Integer>
(在运行T
中List<T>
被解析为Object
),所以唯一可能的数组类型Object[]
.
你可以使用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());
Run Code Online (Sandbox Code Playgroud)
ach*_*ach 16
如果你看一下界面的JavadocList
,你会发现第二种形式toArray
:<T> T[] toArray(T[] a)
.
实际上,Javadoc甚至提供了一个如何完成您想要做的事情的示例:
String[] y = x.toArray(new String[0]);
我可以,并且有时会使用迭代器而不是制作数组,但这对我来说似乎总是很奇怪.为什么get(x)方法知道它返回什么,但toArray()默认为Objects?它就像设计它的一半,他们决定这里不需要这个?
由于问题的目的似乎不仅仅是围绕使用toArray()
泛型,而是要了解ArrayList
类中方法的设计,我想补充:
ArrayList
是一个泛型类,因为它被声明为
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Run Code Online (Sandbox Code Playgroud)
这使得可以使用诸如public E get(int index)
类内的通用方法.
但是如果一个方法toArray()
没有返回E
,E[]
那么事情开始变得有点棘手.不可能提供签名,例如public <E> E[] toArray()
因为无法创建通用数组.
数组的创建发生在运行时,由于类型擦除,Java运行时没有表示类型的特定信息E
.到目前为止,唯一的解决方法是将所需类型作为参数传递给方法,从而将public <T> T[] toArray(T[] a)
客户端强制传递所需类型的签名传递给该方法.
但另一方面,它适用于public E get(int index)
因为如果你看一下方法的实现,你会发现即使该方法使用相同的Object数组来返回指定索引处的元素,它也会被转换为E
E elementData(int index) {
return (E) elementData[index];
}
Run Code Online (Sandbox Code Playgroud)
它是Java编译器这在编译时替换E
用Object
需要注意的是,Java中的数组在运行时知道它们的组件类型.String[]
并且Integer[]
在运行时是不同的类,您可以在运行时向数组询问其组件类型.因此,在运行时需要组件类型(通过在编译时使用new String[...]
或使用Array.newInstance()
并传递类对象对可重新构造的组件类型进行硬编码)来创建数组.
另一方面,泛型中的类型参数在运行时不存在.ArrayList<String>
a和a 之间在运行时完全没有区别ArrayList<Integer>
.这一切都很公正ArrayList
.
这就是为什么你不能仅仅以某种方式单独传递组件类型来获取List<String>
和获取一个基本原因String[]
- 你必须从没有组件类型信息的东西中获取组件类型信息.显然,这是不可能的.
归档时间: |
|
查看次数: |
10669 次 |
最近记录: |