java: (String []) List.toArray () дает ClassCastException

107

Следующий код (запущенный в android) всегда дает мне ClassCastException в третьей строке:

final String[] v1 = i18nCategory.translation.get(id);
final ArrayList<String> v2 = new ArrayList<String>(Arrays.asList(v1));
String[] v3 = (String[])v2.toArray();

Это также происходит, когда v2 имеет значение Object [0], а также когда в нем есть строки. Любая идея, почему?

Гавриил
источник
Вы можете прочитать о Ковариации и Контравариантности - en.wikipedia.org/wiki/…
Hut8
А как насчет случая, когда T - это интерфейс с фабричным методом для создания экземпляра.
sebaj
2
@LaceCard - это очень косвенно связано с ковариацией / контравариантностью. Настоящая проблема в том, что это прямое следствие указанного поведения toArray()метода.
Stephen C

Ответы:

250

Это потому, что когда вы используете

 toArray() 

он возвращает Object [], который не может быть преобразован в String [] (даже если содержимое является строками). Это потому, что метод toArray получает только

List 

и нет

List<String>

поскольку дженерики - это только исходный код, недоступный во время выполнения, поэтому он не может определить, какой тип массива создать.

использовать

toArray(new String[v2.size()]);

который выделяет правильный тип массива (String [] и нужного размера)

ЯБольшойЖирныйГай
источник
3
cuz generics, поэтому
Калеб Брэйси
2
@Kaleb - неправда. По крайней мере, причина не в этом. Эти toArrayметоды существовали до классов коллекций были общими.
Stephen C
10
Это правильное решение, но не правильное объяснение. Вы не можете преобразовать Object [] в Double [], потому что это языковая функция, не более того. Это не связано с дженериками. Вы можете преобразовать Object в Double, предполагая, что это действительно Double. По логике вещей, вы могли бы сделать то же самое с массивом, но это просто не является частью языка. Если вы приведете Object к Double, во время выполнения будет выполняться проверка, чтобы убедиться, что Object действительно является Double. Если вы приведете Double к Object, проверка во время выполнения не будет, поскольку Object находится в иерархии наследования Double.
KyleM 07
5
Делая это в IntelliJ, я получаю предупреждение, которое toArray(new String[0])лучше использовать : «В более старых версиях Java использование массива с предварительно заданным размером было рекомендовано, так как вызов отражения, необходимый для создания массива надлежащего размера, был довольно медленным. Однако, поскольку поздние обновления OpenJDK 6 этот вызов был встроен, что сделало производительность версии с пустым массивом такой же, а иногда и лучше. Кроме того, передача предварительно заданного массива опасна для параллельной коллекции, поскольку между вызовом sizeи возможна гонка toArray».
Mikepote
35

Вы неправильно используете toArray()

Помните, что дженерики Java - это в основном синтаксический сахар. На самом деле ArrayList не знает, что все его элементы являются строками.

Чтобы решить вашу проблему, позвоните toArray(T[]). В твоем случае,

String[] v3 = v2.toArray(new String[v2.size()]);

Обратите внимание, что обобщенная форма toArray(T[])возвращается T[], поэтому нет необходимости явно приводить результат.

Дилум Ранатунга
источник
1
String[] v3 = v2.toArray(new String[0]); 

также делает свой трюк, обратите внимание, что вам даже не нужно больше выполнять приведение, как только методу задан правильный ArrayType.

Надеюсь
источник
0
String[] str = new String[list.size()];
str = (String[]) list.toArray(str);

Используйте вот так.

МакНе
источник
1
Пожалуйста! Отформатируйте свой код! Выделите все это, нажав "ctrl + k", или добавьте "` "перед первой буквой кода и в конце еще одну. Вы также можете выбрать "{}" в справке вверху при написании ответа!
MK