如何将列表的内容传递给varargs方法?

sup*_*ish 5 java variadic-functions

我有一个使用varargs功能的方法:

void add(Animal ...);
Run Code Online (Sandbox Code Playgroud)

现在,.add(dog, cat)我有一个动物列表,其中包含未知数量的元素,而不是

List<Animal> i = new ArrayList<Animal>();
i.add(dog);
i.add(cat);
Run Code Online (Sandbox Code Playgroud)

并希望使用此列表的元素调用add.

我想我可以使用一个数组,但是当我这样做时.add(i.toArray()),它会产生编译错误.

这样做的正确方法是什么?

Tom*_*son 11

它的:

add(i.toArray(new Animal[i.size()]))
Run Code Online (Sandbox Code Playgroud)

List.toArray返回一个Object[],不管List上的类型参数如何:即使你可能会写new List<String>().toArray(),你也会得到一个Object[].但是,使用数组填充的toArray版本返回一个具有正确类型的数组:如果你写new List<String>().toArray(new String[0]),你将得到一个String[].请注意,传入的数组的大小甚至不必与列表的大小相匹配,尽管确保它的确如此.

这最终归因于泛型的一个稍微棘手的特征.乍一看,你可能会认为,String[]List<String>意味着他们的基本类型相似的东西-一个是字符串数组,另一种是字符串列表.

然而,它们实际上是非常不同的.

数组是一种语言基元,并且其类型已烘焙到其中.如果您使用十六进制编辑器并查看JVM内存中的数组实例,您将能够找到(在附近的某个位置)它所拥有的对象类型的记录.这意味着如果您获取某个未知组件类型的数组的实例,您可以找出该类型是什么.相反,这意味着如果您要创建数组实例,则需要知道所需的组件类型.

List,而另一方面,使用泛型,这在Java中与实现类型擦除,这意味着,粗略地讲,它是什么,存在于编译器,而不是在运行时(编译器可以检查你得到它的权利,但是JVM不能).这导致了一个简单而有效的实现(一个简单到足以在不更改JVM的情况下添加到预泛化Java),但它有一些缺点 - 特别是在运行时,没有办法告诉类型参数是什么在泛型类的任何特定实例上,因为类型参数仅存在于编译器中.因为List要处理的是实例toArray(),所以它唯一能做的就是创建一个Object[].它只是不知道要使用的更具体的类型.

查看这个的一种方法是数组有一个类型参数作为其类的一部分,而Lists有一个类型参数作为其类型的一部分,并且由于对象有类但变量有类型,你不能得到类型参数a List来自一个对象,只来自一个持有一个对象的变量(另外,你也不能从一个持有数组的变量中获取数组的类型参数(考虑Object[] array = new String[0];),但这并不重要,因为,变量让你掌握一个对象 - 除非它是空的).

将其归结为代码,问题是:

public <E> E[] createSimilarlyTypedArray(List<E> list) {
    Class<E> componentType = list.???; // there is no way to do this
    return Arrays.newInstance(componentType, list.size());
}
Run Code Online (Sandbox Code Playgroud)