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

时间:2011-09-02 18:04:20

标签: java variadic-functions

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

void add(Animal ...);

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

List<Animal> i = new ArrayList<Animal>();
i.add(dog);
i.add(cat);

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

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

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

3 个答案:

答案 0 :(得分:11)

这是:

add(i.toArray(new Animal[i.size()]))
无论列表中的类型参数如何,

List.toArray都会返回Object[]:即使您可能会写new List<String>().toArray(),也会得到Object[]。但是,version of toArray that takes an array to fill会返回一个类型正确的数组:如果您编写new List<String>().toArray(new String[0]),则会得到String[]。请注意,传入的数组的大小甚至不必与列表的大小相匹配,尽管确保它的确是这样。但

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

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

数组是一种语言基元,并且其类型已烘焙到其中。如果您使用十六进制编辑器并查看JVM内存中的数组实例,您将能够找到(在附近的某个位置)它所拥有的对象类型的记录。这意味着如果您获取某个未知组件类型的数组实例,则可以find out what that type is。相反,这意味着如果你要去create an instance of an array,你需要知道你想要的组件类型。

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

一种看待这种情况的方法是数组的类型参数是的一部分,而List的类型参数是类型的一部分,既然对象有类但变量有类型,你不能从一个对象获取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());
}

答案 1 :(得分:0)

  

当我执行.add(i.toArray())时,它会给出错误,正确的方法是什么   做到了吗?

使用foo.addAll(i)然后根据需要将foo转换为数组。

答案 2 :(得分:0)

您的方法void add(Animal...)需要Animal类的对象或包含Animal对象的数组。你给它一个包含Object类对象的数组。给List一个类似的通用类型:

List<Animal> animals = new ArrayList<Animal>();
animals.add(dog);
animals.add(cat)

然后将列表解析为参数,同时将其转换为数组,方法如下:

add(animals.toArray(new Animal[animals.size()]);

有关泛型的更多信息,请参阅Java API

http://download.oracle.com/javase/1,5.0/docs/guide/language/generics.html