2011-12-13 10 views
20

In realtà, la questione dovrebbe essereCreazione di una serie di collezioni generici

Creating an array of generic anything. 

Perché non è possibile che il compilatore prendersi cura di esso?

Il seguente sarebbe contrassegnato come un errore - impossibile creare un array generico.

List<MyDTO>[] dtoLists = {new ArrayList<MyDTO>(), anExistingDtoList}; 

Per superare questo, ho bisogno di

List<MyDTO>[] dtoLists = (List<MyDTO>[])Array.newInstance(ArrayList.class, 2); 
dtoLists[0] = new ArrayList<MyDTO>(); 
dtoLists[1] = anExistingDtoList; 

Quindi, perché il compilatore non può convertire il primo caso nel secondo caso?

Mi rendo conto che i generici sono determinati in fase di compilazione e non sono determinati in fase di esecuzione, mentre gli array sono determinati in fase di esecuzione e pertanto necessitano di un tipo determinato per creare una matrice.

Quali sono le barriere tecnologiche/logiche che i progettisti di compilatori potrebbero incontrare per impedire che siano in grado di implementarlo?

La questione è puramente filosofica, relativa all'ortogonalità linguistica? In tal caso, in che modo tale comportamento violerebbe l'ortogonalità linguistica?

È una questione di complessità? Spiega la complessità.

Spero che le risposte alla mia domanda mi forniscano informazioni migliori sul comportamento del compilatore Java quando riguarda i generici.

Nota a margine: C'mon smettere di essere felice. Le risposte Array of Generic List non rispondono alla mia domanda. Perché i compilatori non possono eseguire spontaneamente la conversione?

+0

possibile duplicato di [Array of Generic List] (http://stackoverflow.com/questions/7810074/array-of-generic-list) – Thilo

+1

controllare la risposta più alta su quel duplicato. Ha un esempio perché questo non è permesso. – Thilo

+2

Non è un duplicato. La mia domanda richiede risposte sui problemi di progettazione del compilatore. –

risposta

8

realtà Java non creare un array generico per varargs, in modo da poter fare

List<MyDTO>[] dtoLists = array(new ArrayList<MyDTO>(), anExistingDtoList); 

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

Per quanto riguarda il motivo per cui è esplicito creazione dell'array generica proibito, ha qualcosa a che fare con il tipo di cancellazione. (La stessa preoccupazione esiste nella soluzione di cui sopra, ma soppressa da @SafeVarargs) Tuttavia è discutibile; ci sono diversi modi per gestire il problema, un avvertimento del compilatore è probabilmente sufficiente. Ma hanno scelto di vietare a titolo definitivo, probabilmente perché gli array non sono più importanti in ogni caso, ora che abbiamo le collezioni generiche

+0

Gli array in Java sono molto utili per me, essendo un programmatore C++. In Java, non posso passare i puntatori. Devo contenere una variabile in un oggetto o in una matrice, per passarla come un puntatore. –

+0

Voglio dire, prima dei generici, non potremmo fare 'Elenco ', quindi è importante avere 'Foo []' per la tipizzazione statica. Quell'importanza è sparita. Naturalmente gli array sono ancora importanti come base elementare. – irreputable

3

So che, relativamente alle soluzioni alternative a questo problema, Array.newInstance() è un metodo costoso da chiamare. IIRC usa un metodo nativo per istanziare l'array, tra le altre riflessioni coinvolte. Non sono in grado di offrire alcuna statistica, ma questo sembra un buon motivo per cui tale funzionalità non venga automaticamente sostituita dal compilatore per consentire la creazione di array generici. Soprattutto data l'esistenza di ArrayList, ecc., Non sembra un problema urgente.

1

compilatori può spontaneamente eseguire la conversione, sono solo specificato di non perché le matrici generiche non possono comportarsi come matrici non generiche.

Vedi 10.5. Array Store Exception:

Per una matrice cui tipo è A[], dove A è un tipo di riferimento, l'assegnazione ad un componente della matrice viene controllato in fase di esecuzione per assicurare che il valore assegnato è assegnabile al componente.

Se il tipo di valore assegnato non è compatibile con il tipo di componente, viene emesso un numero ArrayStoreException.

Se il tipo di componente di un array non fosse reifiable, la Java Virtual Machine non poteva eseguire il controllo negozio descritto nel paragrafo precedente. Questo è il motivo per cui un'espressione di creazione di array con un tipo di elemento non-reifiable è proibita.

Un List<MyDTO>[] non getterebbe se mettiamo qualche altro tipo di List in esso, in modo che non si comporta come un array. Nota l'ultima frase dalla citazione: "Ecco perché è vietata un'espressione di creazione di array con un tipo di elemento non-reifiable." Questo è il motivo, è specificato che sia così. (E, per la cronaca, this reasoning has always existed, così è stato presente quando la questione è stata pubblicata nel 2011.)

Possiamo ancora fare questo:

@SuppressWarnings({"unchecked","rawtypes"}) 
List<MyDTO>[] dtoLists = new List[] { 
    new ArrayList<MyDTO>(), anExistingDtoList 
}; 

O questo:

@SuppressWarnings("unchecked") 
List<MyDTO>[] dtoLists = (List<MyDTO>[]) new List<?>[] { 
    new ArrayList<MyDTO>(), anExistingDtoList 
}; 

(Oltre a controllare staticamente i tipi di argomento, la cosa varargs è equivalente: è creates a List[] e suppresses warnings.)

Ora, certo, la specifica potrebbe essere cambiata per qualcosa di simile a "Se il tipo di valore assegnato non è compatibile con l'assegnazione con il tipo grezzo di il tipo di componente ...", ma qual è il punto? Salverebbe una manciata di personaggi in alcune situazioni insolite ma altrimenti sopprimerebbe gli avvertimenti per coloro che non capiscono le implicazioni.

Inoltre, ciò che the tutorial e altre spiegazioni tipiche che ho visto non dimostrano è quanto siano integrati gli array covarianti del sistema di tipi.

Ad esempio, data la seguente dichiarazione:

// (declaring our own because Arrays.fill is defined as 
// void fill(Object[], Object) 
// so the next examples would more obviously pass) 
static <T> void fill(T[] arr, T elem) { 
    Arrays.fill(arr, elem); 
} 

Lo sapevate che questo compila?

// throws ArrayStoreException 
fill(new String[1], new Integer(0)); 

E questo compila anche:

// doesn't throw ArrayStoreException 
fill(dtoLists, new ArrayList<Float>()); 

Prima di Java 8, potremmo fare quelle chiamate a fill fallire dandogli la seguente dichiarazione:

static <T, U extends T> void fill(T[] arr, U elem) {...} 

Ma questo era solo un problema con l'inferenza di tipo, e ora funziona "correttamente", mettendo ciecamente List<Float> in un List<MyDTO>[].

Questo si chiama inquinamento mucchio. Può causare il lancio di un numero ClassCastException in un secondo momento, probabilmente da qualche parte completamente estraneo alle azioni che hanno effettivamente causato il problema. L'inquinamento di heap con un contenitore generico come List richiede azioni meno sicure, come l'uso di raw types, ma qui, possiamo causare implicitamente l'inquinamento dell'heap e senza alcun avviso.

Gli array generici (e in realtà gli array in generale) ci forniscono solo il controllo statico nelle circostanze più semplici.

Quindi è evidente che i progettisti di linguaggi hanno pensato che fosse meglio non autorizzarli, ei programmatori che capiscono i problemi che presentano possono sopprimere gli avvertimenti e bypassare la restrizione.

Problemi correlati