java: (String []) List.toArray () donne ClassCastException

107

Le code suivant (exécuté sous Android) me donne toujours une ClassCastException dans la 3ème ligne:

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

Cela se produit également lorsque v2 est Object [0] et également lorsqu'il contient des chaînes. Une idée pourquoi?

Gavriel
la source
Vous voudrez peut-être lire sur la Covariance et la Contravariance - en.wikipedia.org/wiki/…
Hut8
Qu'en est-il du cas où T est une interface avec une méthode d'usine pour l'instanciation.
sebaj
2
@LaceCard - ce n'est que très indirectement lié à la covariance / contravariance. Le vrai problème est qu'il s'agit d'une conséquence directe du comportement spécifié de la toArray()méthode.
Stephen C

Réponses:

250

C'est parce que lorsque vous utilisez

 toArray() 

il renvoie un Object [], qui ne peut pas être converti en String [] (même si le contenu est des Strings) C'est parce que la méthode toArray n'obtient qu'un

List 

et pas

List<String>

car les génériques ne sont qu'un code source et ne sont pas disponibles au moment de l'exécution et ne peuvent donc pas déterminer le type de tableau à créer.

utilisation

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

qui alloue le bon type de tableau (String [] et de la bonne taille)

MoiBigFatGuy
la source
3
cuz generics, c'est pourquoi
Kaleb Brasee
2
@Kaleb - pas vrai. Ou du moins ce n'est pas la raison originale. Les toArrayméthodes existaient avant que les classes de collection ne soient génériques.
Stephen C
10
C'est la bonne solution, mais pas la bonne explication. Vous ne pouvez pas convertir Object [] en Double [] car c'est une fonctionnalité de langage, rien de plus. Cela n'a pas à voir avec les génériques. Vous pouvez convertir Object en Double en supposant qu'il s'agit vraiment d'un Double. Donc, logiquement, vous pourriez faire la même chose avec un tableau, mais cela ne fait tout simplement pas partie du langage. Si vous lancez Object en Double, une vérification d'exécution sera effectuée pour vous assurer que Object EST réellement un Double. Si vous transtypez Double en Object, il n'y a pas de vérification à l'exécution, car Object est dans la hiérarchie d'héritage de Double.
KyleM
5
Lorsque je fais cela dans IntelliJ, je reçois un avertissement à utiliser toArray(new String[0])plutôt: "Dans les anciennes versions de Java, l'utilisation d'un tableau pré-dimensionné était recommandée car l'appel de réflexion nécessaire pour créer un tableau de taille appropriée était assez lent. Cependant, depuis les dernières mises à jour d'OpenJDK 6 cet appel était intrinsèque, rendant les performances de la version de tableau vide les mêmes et parfois même meilleures. Le passage d'un tableau pré-dimensionné est également dangereux pour une collection concurrente car une course est possible entre l' appel sizeet toArray. "
Mikepote
35

Vous utilisez le mauvais toArray()

N'oubliez pas que les génériques de Java sont principalement du sucre syntaxique. Un ArrayList ne sait pas réellement que tous ses éléments sont des chaînes.

Pour résoudre votre problème, appelez toArray(T[]). Dans ton cas,

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

Notez que le formulaire générique est toArray(T[])renvoyé T[], le résultat n'a donc pas besoin d'être explicitement converti.

Dilum Ranatunga
la source
1
String[] v3 = v2.toArray(new String[0]); 

fait également l'affaire, notez que vous n'avez même plus besoin de lancer un cast une fois que le bon ArrayType est donné à la méthode.

Avec un peu de chance
la source
0
String[] str = new String[list.size()];
str = (String[]) list.toArray(str);

Utilisez comme ça.

MakNe
la source
1
S'il vous plaît! Formatez votre code! Sélectionnez tout cela en appuyant sur "ctrl + k", ou ajoutez "` "avant la première lettre de code et au dernier un autre. Vous pouvez également sélectionner "{}" dans l'aide en haut lors de la rédaction de la réponse!
MK