2011-09-02 2 views
5

У меня есть метод, который использует переменные аргументы особенность:Как передать содержимое списка методу varargs?

void add(Animal ...); 

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

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

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

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

Каков правильный способ?

+0

Я не могу понять вопрос. Вы создали свой собственный метод'add (Animal ...) ', но вы вызываете ArrayList.add (Object), а не свой собственный. Почему это? Пожалуйста, отредактируйте вопрос в контексте кода. Этот блок кода внутри вашего метода «адд»? Если ваша единственная проблема заключается в том, что вы не можете бросить объект [] в Animal [], то выполните ответ @Tom. – aalku

+0

Просто заметьте на будущее: если у вас есть ошибка компилятора, скопируйте полное сообщение об ошибке, а также строки исходного кода, относящиеся к этому сообщению. –

ответ

10

Это:

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

Один способа смотреть на это, что массивы имеют аргумент типа как часть их класса, в то время как List s имеет аргумент типа как часть их типа, и так как объекты имеют классы, но переменные имеют тип, вы не можете получить аргумент типа 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

Он будет использовать ваш массив, если он достаточно большой или выделяет новый, если это не так. – aalku

+0

не получишь исключение типа при вызове i.toArray (новое Animal []) в списке, который не имеет набора общих типов? –

+1

@Benjamin: нет, вы не будете. Попробуйте запустить «Массивы». asList ("hello"). ToArray (новый String [1]); '- он отлично работает. Статически это прекрасно, потому что эта версия 'toArray' выбирает свой параметр типа по своему параметру, а не собственный« Список »(странный, но правильный!) И динамически, это нормально, потому что во время выполнения тип' List' был удален. –

0

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

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

0

Ваш метод void add(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

+1

Больше, чем ожидать «массив с объектами« Animal »в нем», он ожидает массив, собственный тип которого «Animal []». Форма с нулевым аргументом 'toArray' создает массив с объектами Animal в нем - массив, который в основном представляет собой новый Object [] {new Animal()}'. Тем не менее, необходимо «new Animal [] {new Animal()}». Слияние между массивами и дженериками - это действительно предательский участок воды! –

Смежные вопросы