2011-10-18 13 views
31

sto giocando con Generico e gli array, sembra che il seguente codice compila bene,Array di elenco generico

ArrayList<Key> a = new ArrayList<Key>(); 

Ma il compilatore si lamenta di questo,

ArrayList<Key>[] a = new ArrayList<Key>[10]; 

Con la lettura di posta in StackOverflow , In qualche modo capisco che ciò è dovuto a Type Erasure e posso correggerlo usando,

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10]; 

o elenco di Lista

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>(); 

Ma io non riesco a capire il motivo dietro la scena. Soprattutto, perché il secondo è illegale, dato che il primo è perfettamente OK. E perché il compilatore non si lamenta della lista di lista.

+3

http://stackoverflow.com/questions/217065/cannot-create-an-array-of-linkedlists-in-java – tcb

risposta

22

Non è possibile avere un array, poiché un array richiede un tipo non elaborato. Lo hai tipizzato in seconda istanza, il che lo rende adatto al tipo definito ed è quindi legale (tuttavia, è impossibile per questo inferire). L'elenco delle liste è legale come ArrayList non è un array.

Leggere il capitolo 7.3 (pagina 15) nel numero official tutorial per ulteriori dettagli.

Il tipo di componente di un oggetto array non può essere una variabile di tipo o di un tipo parametrizzato , a meno che sia un (illimitata) jolly type.You può tipi array di dichiarazione cui tipo elemento è una variabile di tipo o Tipo di parametro , ma non di array. Questo è fastidioso, per essere sicuro. Questa restrizione è necessaria per evitare situazioni come:

List<String>[] lsa = new List<String>[10]; // not really allowed 
Object o = lsa; 
Object[] oa = (Object[]) o; 
List<Integer> li = new ArrayList<Integer>(); 
li.add(new Integer(3)); 
oa[1] = li; // unsound, but passes run time store check 
String s = lsa[1].get(0); // run-time error - ClassCastException 

Se sono stati autorizzati matrici di tipo parametrico, l'esempio descritto sopra compilare senza avvisi incontrollati, e tuttavia non in fase di esecuzione.

Il tutorial passa poi a dire quanto segue:

Poiché le variabili di tipo non esistono in fase di esecuzione, non v'è alcun modo per determinare il tipo di matrice effettiva sarebbe. Il modo per aggirare questo tipo di limitazioni è quello di utilizzare letterali di classe come gestite il tempo tipo gettoni

1

Gli array consentono di sfuggire controlli di tipo (come illustrato nella risposta di Chris). Quindi, potresti avere un codice che passa tutti i controlli del compilatore (nessun avvertimento "non controllato" dal compilatore), ma fallisce in fase di esecuzione con ClassCastException. Proibire questa costruzione solleva il problema per uno sviluppatore, quindi appaiono gli avvisi.

4

Avevo uno similar question anch'io - FWIW, non ho trovato le risposte persuasive. La sezione pertinente della risposta più dettagliato (riferendosi al riferimento pdf) è:

il tipo di componente di un oggetto array non può essere una variabile di tipo o di un tipo parametrizzato , a meno che sia un (illimitata) tipo di carattere jolly.È possibile dichiarare i tipi di matrice il cui tipo di elemento è una variabile di tipo o un tipo con parametri di tipo ma non gli oggetti di matrice. Questo è fastidioso, per essere sicuro . Questa restrizione è necessaria per evitare situazioni come

 List<String>[] lsa = new List<String>[10]; // not really allowed 
     Object o = lsa; 
     Object[] oa = (Object[]) o; 
     List<Integer> li = new ArrayList<Integer>(); 
     li.add(new Integer(3)); 
     oa[1] = li; // unsound, but passes run time store check 
     String s = lsa[1].get(0); // run-time error - ClassCastException 

Quindi perché non posso gatto l'elenco [] per Object [], quindi spingere qualcosa di sbagliato nella Object [], quindi fare riferimento in modo errato dal riferimento List, attraverso il cast cast, questo è cattivo/non consentito? Ma solo con nuovo?

E 'ancora più che un po' oscuro per me come dichiarare questo con nuovo è più o meno un problema che l'utilizzo, ancora incrociando i miei occhi fissandolo nella speranza che inizi a dare un senso, o in meno risolti in una bella immagine 3d.

+0

compilatore deve garantire la sicurezza del tipo generico; quando non può, deve o non consentire il codice o emettere un avviso. nell'esempio, un avvertimento in seconda linea sembra sufficiente; la decisione di java di escludere completamente la creazione di array generici sembra troppo dura. – irreputable

+1

Sì 'ArrayList [] a = (ArrayList []) new ArrayList [10]; 'ha ancora lo stesso problema, ma rende più chiaro che stai imbrogliando il sistema di tipi (e genera un avvertimento.) – finnw

+0

finnw, il tuo commento è penso che la risposta giusta e l'unico modo in cui questo abbia senso per me. –

2

La creazione di array generici non è sicura per il tipo (consultare "Elemento 25: liste di preferenza per gli array" di "Java efficace - seconda edizione" di Joshua Bloch).

Usa:

List<List<Key>> b = new ArrayList<List<Key>>(10); 

O con Java SE 7:

List<List<Key>> b = new ArrayList<>(10); 
4

Array era generici povero dell'uomo; con generici veri, si dovrebbero evitare gli array, anche se non sempre possibile.

Le matrici sono covarianti, i generici sono invarianti; combinato con la cancellazione, le cose non si adattano molto bene, come illustrato dall'esempio nella risposta di Chris.

Tuttavia penso che sia possibile rilassare le specifiche per consentire la creazione di array generici - non c'è davvero alcun problema lì. Il pericolo arriva quando si lancia la matrice; un avvertimento del compilatore a quel punto è sufficiente.

In realtà Java crea array generici per i metodi vararg, quindi è un po 'ipocrita.

Qui ci sono metodi di utilità approfittando di questo fatto

@SafeVarargs 
static <E> E[] arrayLiteral(E... array) 
{ 
    return array; 
} 

@SafeVarargs 
static <E> E[] newArray(int length, E... array) 
{ 
    return Arrays.copyOf(array, length); 
} 

// usage 

    List<String>[] array1 = arrayLiteral(list, list); 

    List<String>[] array2 = newArray(10); 
+0

Probabilmente è a causa della retrocompatibilità. Supponiamo che alcune versioni future (Java 9?) Consentano la creazione di array generici, quindi quando il codice Java 9 si mescola con il codice Java 5/6/7/8 (che può matrici di upcast senza avviso) il risultato sarà ClassCastExceptions imprevisto. – finnw

+0

Questo non funziona se 'E' è una variabile di tipo. Il varargs crea una matrice di cancellazione di 'E' quando' E' è una variabile di tipo, rendendola non molto diversa da '(E []) new Object [n]'. Si prega di consultare [http://ideone.com/T8xF91](http://ideone.com/T8xF91). – Radiodef