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


107

Il codice seguente (eseguito in Android) mi dà sempre un'eccezione ClassCastException nella terza riga:

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

Succede anche quando v2 è Object [0] e anche quando ci sono stringhe in esso. Qualche idea perché?


Puoi leggere informazioni su Covarianza e controvarianza - en.wikipedia.org/wiki/…
Hut8

E il caso in cui T è un'interfaccia con un metodo factory per l'istanza.
sebaj

2
@LaceCard - questo è solo molto indirettamente correlato alla covarianza / controvarianza. Il vero problema è che questa è una conseguenza diretta del comportamento specificato del toArray()metodo.
Stephen C

Risposte:


250

Questo perché quando usi

 toArray() 

restituisce un Object [], che non può essere convertito in String [] (anche se i contenuti sono stringhe) Questo perché il metodo toArray ottiene solo un

List 

e non

List<String>

poiché i generici sono solo una cosa del codice sorgente e non sono disponibili in fase di esecuzione e quindi non possono determinare quale tipo di array creare.

uso

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

che alloca il giusto tipo di array (String [] e della giusta dimensione)


3
perché generici, ecco perché
Kaleb Brasee

2
@ Kaleb - non vero. O almeno non è questo il motivo originale. I toArraymetodi esistevano prima che le classi di raccolta fossero generiche.
Stephen C

10
Questa è la soluzione corretta, ma non la spiegazione corretta. Non puoi lanciare Object [] su Double [] perché è una funzionalità del linguaggio, niente di più. Non ha a che fare con i generici. Puoi lanciare Object to Double assumendo che sia veramente un Double. Quindi, logicamente, potresti fare lo stesso con un array, ma semplicemente non fa parte del linguaggio. Se esegui il cast di Object su Double, verrà eseguito un controllo di runtime per assicurarti che Object sia effettivamente un Double. Se esegui il cast di Double su Object, non viene eseguito alcun controllo di runtime, poiché Object è nella gerarchia di ereditarietà di Double.
KyleM

5
Quando si esegue questa operazione in IntelliJ, viene visualizzato un avviso da utilizzare toArray(new String[0])piuttosto: "Nelle versioni precedenti di Java si consigliava di utilizzare un array pre-dimensionato poiché la chiamata di riflessione necessaria per creare un array di dimensioni adeguate era piuttosto lenta. Tuttavia, poiché gli ultimi aggiornamenti di OpenJDK 6 questa chiamata è stata intrinseca, rendendo le prestazioni della versione dell'array vuoto uguali e talvolta anche migliori. Anche il passaggio di un array pre-dimensionato è pericoloso per una raccolta simultanea poiché è possibile una gara tra la chiamata sizee toArray. "
Mikepote

35

Stai usando il torto toArray()

Ricorda che i generici di Java sono principalmente zucchero sintattico. Un ArrayList in realtà non sa che tutti i suoi elementi sono stringhe.

Per risolvere il tuo problema, chiama toArray(T[]). Nel tuo caso,

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

Si noti che il modulo generico toArray(T[])restituisce T[], quindi non è necessario eseguire il cast esplicito del risultato.


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

fa anche il trucco, nota che non hai nemmeno bisogno di eseguire più il cast una volta che il giusto ArrayType è stato dato al metodo.


0
String[] str = new String[list.size()];
str = (String[]) list.toArray(str);

Usa in questo modo.


1
Per favore! Formatta il tuo codice! Selezionalo tutto premendo "ctrl + k", o aggiungi "` "prima della prima lettera del codice e all'ultima un'altra. Puoi anche selezionare "{}" nella guida in alto quando scrivi la risposta!
MK
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.