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


107

Наступний код (запускається в android) завжди дає мені ClassCastException у 3-му рядку:

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

Відповіді:


249

Це тому, що коли ви користуєтесь

 toArray() 

він повертає Object [], який не може бути передано до String [] (навіть вміст є Strings) Це тому, що метод toArray отримує лише

List 

і ні

List<String>

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

використання

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

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


3
бо дженерик, тому
Калеб Брезе

2
@Kaleb - неправда. Або, принаймні, це не первісна причина. Ці toArrayметоди існували до класів колекцій були загальними.
Stephen C

10
Це правильне рішення, але не правильне пояснення. Ви не можете привласнити Object [] до Double [], тому що це мовна особливість, нічого більше. Це не пов'язане з Generics. Ви можете кинути "Об'єкт на подвійний", вважаючи, що це справді подвійний. Так що логічно, ви можете зробити те ж саме з масивом, але це просто не є частиною мови. Якщо ви передасте "Об'єкт" в "Подвійний", буде встановлена ​​перевірка виконання, щоб переконатися, що "Об'єкт" насправді є подвійним. Якщо ви додали Double до Object, перевірки часу виконання не відбувається, оскільки Object знаходиться в ієрархії спадкування Double.
KyleM

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" або додайте "" "перед першою літерою коду, а на останній - ще однією. Ви також можете вибрати "{}" у довідці вгорі під час написання відповіді!
МК
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.