Perché i generici in Java funzionano con le classi ma non con i tipi primitivi?
Ad esempio, funziona perfettamente:
List<Integer> foo = new ArrayList<Integer>();
ma questo non è permesso:
List<int> bar = new ArrayList<int>();
Perché i generici in Java funzionano con le classi ma non con i tipi primitivi?
Ad esempio, funziona perfettamente:
List<Integer> foo = new ArrayList<Integer>();
ma questo non è permesso:
List<int> bar = new ArrayList<int>();
Risposte:
I generici in Java sono un costrutto interamente in fase di compilazione: il compilatore trasforma tutti gli usi generici in cast nel giusto tipo. Questo per mantenere la retrocompatibilità con i precedenti runtime JVM.
Questo:
List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);
viene trasformato in (approssimativamente):
List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);
Quindi, tutto ciò che viene utilizzato come generici deve essere convertibile in oggetto (in questo esempio get(0)
restituisce un Object
), mentre i tipi primitivi non lo sono. Quindi non possono essere usati in generici.
In Java, i generici funzionano come fanno ... almeno in parte ... perché sono stati aggiunti alla lingua alcuni anni dopo che la lingua è stata progettata 1 . I progettisti del linguaggio erano limitati nelle loro opzioni per i generici dovendo elaborare un design che fosse retrocompatibile con il linguaggio esistente e la libreria di classi Java .
Altri linguaggi di programmazione (ad es. C ++, C #, Ada) consentono l'utilizzo di tipi primitivi come tipi di parametri per generici. Ma il rovescio della medaglia nel fare questo è che le implementazioni di tali generici (o tipi di template) in genere comportano la generazione di una copia distinta del tipo generico per ogni parametrizzazione di tipo.
1 - Il motivo per cui i generici non sono stati inclusi in Java 1.0 è stato a causa della pressione del tempo. Sentivano di dover rilasciare rapidamente il linguaggio Java per riempire le nuove opportunità di mercato presentate dai browser web. James Gosling ha dichiarato che gli sarebbe piaciuto includere i generici se avessero avuto il tempo. Come sarebbe apparso il linguaggio Java se questo fosse accaduto è la supposizione di chiunque.
In java i generici sono implementati usando "Type erasure" per la retrocompatibilità. Tutti i tipi generici vengono convertiti in Object in fase di runtime. per esempio,
public class Container<T> {
private T data;
public T getData() {
return data;
}
}
verrà visualizzato in fase di esecuzione come,
public class Container {
private Object data;
public Object getData() {
return data;
}
}
il compilatore è responsabile di fornire il cast adeguato per garantire la sicurezza del tipo.
Container<Integer> val = new Container<Integer>();
Integer data = val.getData()
diventerà
Container val = new Container();
Integer data = (Integer) val.getData()
Ora la domanda è: "Oggetto" viene scelto come tipo in fase di esecuzione?
La risposta è oggetto è la superclasse di tutti gli oggetti e può rappresentare qualsiasi oggetto definito dall'utente.
Poiché tutte le primitive non ereditano da " Object ", quindi non possiamo usarlo come tipo generico.
Cordiali saluti: il progetto Valhalla sta cercando di risolvere il problema sopra riportato.
Le raccolte sono definite per richiedere un tipo da cui deriva java.lang.Object
. I tipi di base semplicemente non lo fanno.
Secondo la documentazione Java , le variabili di tipo generico possono essere istanziate solo con tipi di riferimento, non con tipi primitivi.
Ciò dovrebbe avvenire in Java 10 sotto Project Valhalla .
Nel documento di Brian Goetz su stato della specializzazione
C'è un'eccellente spiegazione del motivo per cui il generico non è stato supportato per la primitiva. E come verrà implementato nelle versioni future di Java.
L'attuale implementazione di Java cancellata che produce una classe per tutte le istanze di riferimento e nessun supporto per le istanze primitive. (Questa è una traduzione omogenea e la restrizione che i generici di Java possono variare solo rispetto ai tipi di riferimento deriva dai limiti della traduzione omogenea rispetto all'insieme di bytecode della JVM, che utilizza diversi bytecode per le operazioni sui tipi di riferimento rispetto ai tipi primitivi.) Tuttavia, i generici cancellati in Java forniscono sia parametricità comportamentale (metodi generici) che parametricità dei dati (istanze raw e jolly di tipi generici).
...
è stata scelta una strategia di traduzione omogenea, in cui le variabili di tipo generico vengono cancellate al limite quando vengono incorporate nel bytecode. Ciò significa che, indipendentemente dal fatto che una classe sia generica, viene comunque compilata in un'unica classe, con lo stesso nome e le cui firme dei membri sono uguali. La sicurezza del tipo viene verificata al momento della compilazione e il runtime non è vincolato dal sistema di tipi generico. A sua volta, ciò ha imposto la restrizione che i generici potevano funzionare solo sui tipi di riferimento, poiché Object è il tipo più generale disponibile e non si estende ai tipi primitivi.
Quando si crea un oggetto, non è possibile sostituire un tipo primitivo con il parametro type. Per questo motivo, si tratta di un problema di implementazione del compilatore. I tipi primitivi hanno le loro istruzioni bytecode per il caricamento e l'archiviazione nello stack della macchina virtuale. Quindi non è impossibile compilare generici primitivi in questi percorsi bytecode separati, ma renderebbe il compilatore complicato.