2009-04-14 13 views
45

Voglio creare un array di Classi, ognuno dei quali rappresenta un tipo disponibile nel sistema che sto costruendo. Tutte le classi coinvolte sono sottoclassi di una superclasse comune. Così mi piacerebbe fare:Come si usano i generici con una serie di Classi?

Class<? extends SuperClass>[] availableTypes = { SubClass1.class, SubClass2.class }; 

Questo mi dà l'errore:

Cannot create a generic array of Class<? extends SuperClass>. 

ottengo lo stesso messaggio se provo a qualificare la creazione della matrice, sul lato destro della inizializzazione:

Class<? extends SuperClass>[] availableTypes = Class<? extends SuperClass>[] { SubClass1.class, SubClass2.class }; 

posso ottenere il codice per compilare se elimino i generici qualifiche:

Class[] availableTypes = { SubClass1.class, SubClass2.class }; 

Ma poi ricevo l'avviso generici:

Classe è un tipo non elaborato. I riferimenti al tipo generico Classe devono essere parametrizzati.

Sto provando; Sto provando! :) Inoltre, a questo punto, anche se questo non ha provocato un avvertimento, ho perso un pezzo dell'interfaccia che stavo cercando di definire. Non voglio semplicemente restituire una serie di classi arbitrarie; Voglio restituire una serie di classi che sono tutte sottoclassi di una particolare SuperClass!

Eclipse ha alcuni strumenti piuttosto potenti per capire quali parametri utilizzare per correggere le dichiarazioni generiche, ma in questo caso cade, come tende a fare quando si ha a che fare con Class. La procedura "Inferenti argomenti di tipo generico" che offre non cambia affatto il codice, lasciando l'avviso.

sono stato in grado di ovviare a questo utilizzando una collezione invece:

List<Class<? extends SuperClass>> availableTypes = new List<Class<? extends SuperClass>>(); 

Ma qual è il modo giusto per fare questo con gli array?

risposta

22

Sembra un po 'disfattista, ma problemi come questo sono proprio il motivo per cui la maggior parte delle persone evita di mescolare array e generici. A causa del modo in cui i generici sono implementati (type erasure), gli array e i generici non funzioneranno mai bene insieme.

due soluzioni:

  • asta utilizzando un insieme (ad esempio ArrayList<Class<? extends SuperClass>>), che funziona altrettanto bene come un array e permette anche l'espansione.
  • Inserire un'annotazione @SuppressWarnings("unchecked") nel codice che crea l'array insieme a un commento che ne giustifichi l'utilizzo.
2

Il problema è che è illegale creare un array di un tipo generico. L'unico modo per aggirarlo è tramite il casting sul tipo generico quando si crea l'array, ma questa non è una soluzione molto buona. (Si noti che è possibile uso una matrice generica, solo che non crearne uno:. Vedi this question)

Si dovrebbe quasi sempre utilizzando gli elenchi invece di array in ogni caso, quindi penso che si avvicinò con la migliore soluzione già .

14

Utilizzare questa sintassi:

Class<? extends SuperClass>[] avail = new Class[] { SubClass1.class, ... }; 

vi darà un avvertimento "incontrollato", e giustamente, poiché potrebbe essere compresi un oggetto Class per un tipo che non si estende SuperClass nella matrice.

11

Il modo corretto per fare ciò con gli array è farlo con una raccolta. Scusate! Per una serie complicata di motivi, gli array non giocano bene con i generici. Le matrici hanno un modello di covarianza diverso rispetto a quello degli oggetti generici, che alla fine causano i problemi in cui ti stai imbattendo. Ad esempio, con gli array, ma non (normalmente) con oggetti generici, si può legalmente fare questo:

Object[] myArray = new String[5]; 

mentre non si può fare questo:

LinkedList<Object> myCollection = new LinkedList<String>(); 

Se volete maggiori dettagli, si può vedere Nella pagina Arrays In Java Generics dalla eccellente Generics FAQ

come simonn detto questo, si può anche basta usare gli array come sono, e utilizzare @SuppressWarnings("unchecked") per mettere a tacere gli avvertimenti. Questo funzionerà, ma senza il tipo di sicurezza che i generici possono fornirti. Se si tratta di prestazioni di cui si è preoccupati, basta usare un ArrayList in modo da utilizzare solo un involucro sottile attorno a un array, ma con tutte le garanzie di sicurezza di tipo fornite dai generici.

+1

covarianza! Hai detto la parolaccia! –

4

But what's the right way to do this with arrays?

Non esiste un modo sicuro per farlo; utilizzando la raccolta è l'approccio giusto. Per capire perché, immagina se fosse permesso. Si potrebbe avere una situazione come questa:

// Illegal! 
Object[] baskets = new FruitBasket<? extends Citrus>[10]; 

// This is okay. 
baskets[0] = new FruitBasket<Lemon>(); 

// Danger! This should fail, but the type system will let it through. 
baskets[0] = new FruitBasket<Potato>(); 

Il sistema tipo ha bisogno di rilevare se un paniere che viene aggiunto alla matrice è di tipo FruitBasket<? extends Citrus> o un sottotipo. Un FruitBasket non corrisponde e dovrebbe essere rifiutato con un ArrayStoreException. Ma non succede niente!

A causa della cancellazione dei tipi, la JVM può vedere solo il tipo di runtime dell'array. In fase di runtime, dobbiamo confrontare il tipo di array con il tipo di elemento per accertarci che corrispondano. Il tipo di componente runtime dell'array è FruitBasket[] dopo la cancellazione del tipo; allo stesso modo, il tipo di runtime dell'elemento è FruitBasket. Nessun problema verrà rilevato - ed è per questo che è pericoloso.

0

Non SICURO tipo e con avvertimento getto incontrollato:

Class<? extends SuperClass>[] availableTypes = 
    (Class<? extends SuperClass>[]) 
    (new Class[]{ SubClass1.class, SubClass2.class }); 
Problemi correlati