Это:
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());
}