Ecco come utilizzare generics per ottenere un array esattamente del tipo che stai cercando, preservando la sicurezza del tipo (al contrario delle altre risposte, che ti restituiranno un Object
array o genereranno avvisi al momento della compilazione):
import java.lang.reflect.Array;
public class GenSet<E> {
private E[] a;
public GenSet(Class<E[]> clazz, int length) {
a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));
}
public static void main(String[] args) {
GenSet<String> foo = new GenSet<String>(String[].class, 1);
String[] bar = foo.a;
foo.a[0] = "xyzzy";
String baz = foo.a[0];
}
}
Questo si compila senza avvertimenti e, come puoi vedere main
, per qualunque tipo tu dichiari un'istanza di GenSet
as, puoi assegnarlo a
a un array di quel tipo e puoi assegnare un elemento a
a una variabile di quel tipo, il che significa che l'array e i valori nella matrice sono del tipo corretto.
Funziona usando i letterali di classe come token di tipo runtime, come discusso nei Tutorial Java . I letterali di classe vengono trattati dal compilatore come istanze di java.lang.Class
. Per usarne uno, basta seguire il nome di una classe con .class
. Quindi, String.class
agisce come un Class
oggetto che rappresenta la classe String
. Questo funziona anche per interfacce, enumerazioni, array di qualsiasi dimensione (ad es. String[].class
), Primitive (ad es. int.class
) E la parola chiave void
(ad es void.class
.).
Class
stesso è generico (dichiarato come Class<T>
, dove T
sta per il tipo che l' Class
oggetto rappresenta), nel senso che il tipo di String.class
è Class<String>
.
Quindi, ogni volta che si chiama il costruttore per GenSet
, si passa in una classe letterale per il primo argomento che rappresenta una matrice del GenSet
tipo dichiarato dell'istanza (ad esempio String[].class
per GenSet<String>
). Nota che non sarai in grado di ottenere una matrice di primitive, poiché le primitive non possono essere utilizzate per le variabili di tipo.
All'interno del costruttore, chiamando il metodo viene cast
restituito il Object
cast dell'argomento passato alla classe rappresentata Class
dall'oggetto su cui è stato chiamato il metodo. La chiamata del metodo statico newInstance
in java.lang.reflect.Array
restituisce come una Object
matrice del tipo rappresentato Class
dall'oggetto passato come primo argomento e della lunghezza specificata dal int
passato come secondo argomento. Chiamando il metodo getComponentType
restituisce un Class
oggetto che rappresenta il tipo di componenti della matrice rappresentata dalla Class
oggetto su cui è stato chiamato il metodo (ad esempio String.class
per String[].class
, null
se l' Class
oggetto non rappresenta un array).
L'ultima frase non è del tutto accurata. La chiamata String[].class.getComponentType()
restituisce un Class
oggetto che rappresenta la classe String
, ma il suo tipo Class<?>
non Class<String>
è, motivo per cui non è possibile eseguire operazioni come le seguenti.
String foo = String[].class.getComponentType().cast("bar"); // won't compile
Lo stesso vale per ogni metodo Class
che restituisce un Class
oggetto.
Per quanto riguarda il commento di Joachim Sauer su questa risposta (non ho abbastanza reputazione per commentarlo da solo), l'esempio che usa il cast per T[]
genererà un avviso perché il compilatore non può garantire la sicurezza del tipo in quel caso.
Modifica in merito ai commenti di Ingo:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}