Как я могу передать содержимое списка в метод varargs? - PullRequest
5 голосов
/ 02 сентября 2011

У меня есть метод, который использует функцию varargs:

void add(Animal ...);

Теперь вместо .add(dog, cat) у меня есть список животных с неизвестным количеством элементов,

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

и хотите вызвать add с элементами этого списка.

Я думаю, что мог бы использовать массив, но когда я делаю .add(i.toArray()), это выдает ошибку компилятора.

Как правильно это сделать?

Ответы [ 3 ]

11 голосов
/ 02 сентября 2011

Это:

add(i.toArray(new Animal[i.size()]))

List.toArray возвращает Object[] независимо от аргумента типа в Списке: даже если вы напишите new List<String>().toArray(), вы получите Object[]. Однако версия toArray , которая принимает массив для заполнения , возвращает массив с правильным типом: если вы напишите new List<String>().toArray(new String[0]), вы получите String[]. Обратите внимание, что размер передаваемого массива даже не должен совпадать с размером списка, хотя рекомендуется убедиться, что это так.

Это, в конечном счете, объясняется слегка хитрой особенностью дженериков. На первый взгляд, вы можете подумать, что String[] и List<String> означают аналогичные вещи для их базовых типов - один представляет собой массив строк, а другой - список строк.

Однако на самом деле они очень разные.

Массив является языковым примитивом, и его тип запекается в нем. Если вы взяли шестнадцатеричный редактор и посмотрели на экземпляр массива в памяти в JVM, вы сможете найти (где-то поблизости) запись о типе объектов, которые он содержит. Это означает, что если вы берете экземпляр массива какого-либо неизвестного типа компонента, вы можете узнать, что это за тип . И наоборот, это означает, что если вы собираетесь создать экземпляр массива , вам нужно знать, какой тип компонента вы хотите.

В List, с другой стороны, используются обобщения, которые в Java реализованы с стиранием типа , что означает, что, грубо говоря, это то, что существует в компиляторе, но не в время выполнения (компилятор может проверить, правильно ли вы это делаете, но JVM не может). Это приводит к простой и эффективной реализации (достаточно простой, чтобы быть добавленной в Java-пре-генерики без изменения JVM), но у нее есть некоторые недостатки - в частности, что во время выполнения нет способа определить, что такое аргумент типа на любой конкретный экземпляр универсального класса, потому что аргументы типа существуют только в компиляторе. Поскольку обрабатывать toArray() должен экземпляр List, единственное, что он может сделать, - это создать 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());
}
0 голосов
/ 02 сентября 2011

Ваш метод void add(Animal...) ожидает объект класса Animal или массив с объектами Animal в нем. Вы даете ему массив с объектами класса Object. Присвойте списку универсальный тип примерно так:

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

0 голосов
/ 02 сентября 2011

когда я делаю .add (i.toArray ()), он выдает ошибку, что является правильным способом сделать это?

Используйте foo.addAll(i), а затем при необходимости преобразуйте foo в массив.

...