So verwenden Sie Generika, um ein Array mit genau dem Typ zu erhalten, nach dem Sie suchen, während die Typensicherheit erhalten bleibt (im Gegensatz zu den anderen Antworten, die Ihnen entweder ein ObjectArray zurückgeben oder beim Kompilieren zu Warnungen führen):
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];
}
}
Dies wird ohne Warnungen kompiliert, und wie Sie in sehen können main, können Sie für jeden Typ, für den Sie eine Instanz GenSetals deklarieren a, einem Array dieses Typs zuweisen und ein Element aus zuweisena zu einer Variable dieses Typs, dass das Array bedeutet und die Werte im Array sind vom richtigen Typ.
Es funktioniert mit Klassenliteralen als Laufzeit-Token, wie in den Java-Tutorials beschrieben . Klassenliterale werden vom Compiler als Instanzen von behandelt java.lang.Class. Um eine zu verwenden, folgen Sie einfach dem Namen einer Klasse mit .class. So String.classwirkt wie ein ClassObjekt der Klasse darstellt String. Dies funktioniert auch für Schnittstellen, Aufzählungen, beliebige dimensionale Arrays (z. B. String[].class) , Grundelemente (z. B. int.class) und das Schlüsselwort void(dh void.class).
Classselbst ist generisch (deklariert als Class<T>, wobei Tfür den Typ steht, den das ClassObjekt darstellt), was bedeutet, dass der Typ von String.classistClass<String> .
Wenn Sie also den Konstruktor für aufrufen GenSet, übergeben Sie ein Klassenliteral für das erste Argument, das ein Array des GenSetdeklarierten Typs der Instanz darstellt (z. B. String[].classfürGenSet<String> . ). Beachten Sie, dass Sie kein Array von Grundelementen abrufen können, da Grundelemente nicht für Typvariablen verwendet werden können.
Innerhalb des Konstruktors gibt der Aufruf der Methode castdas übergebene ObjectArgument an die Klasse zurück, die durch das ClassObjekt dargestellt wird, für das die Methode aufgerufen wurde. Das Aufrufen der statischen Methode newInstancein java.lang.reflect.Arraygibt als ObjectArray den Typ zurück, der durch das Classals erstes Argument übergebene Objekt und die durch das intals zweites Argument übergebene Länge angegebene Länge dargestellt wird . Der Aufruf der Methode getComponentTypeGibt ein ClassObjekt den Komponententyp des Arrays von dem dargestellten darstellt ClassObjekt , auf dem das Verfahren aufgerufen wurde (zB String.classfür die String[].class, nullwenn dieClass Objekt nicht ein Array darstellt).
Dieser letzte Satz ist nicht ganz richtig. Beim Aufrufen wird String[].class.getComponentType()ein ClassObjekt zurückgegeben, das die Klasse darstellt String, sein Typ jedoch Class<?>nicht Class<String>. Aus diesem Grund können Sie Folgendes nicht ausführen.
String foo = String[].class.getComponentType().cast("bar"); // won't compile
Gleiches gilt für jede Methode Class, die ein ClassObjekt zurückgibt .
In Bezug auf Joachim Sauers Kommentar zu dieser Antwort (ich habe nicht genug Ruf, um sie selbst zu kommentieren) führt das Beispiel, in dem die Besetzung verwendet T[]wird, zu einer Warnung, da der Compiler in diesem Fall keine Typensicherheit garantieren kann.
Bearbeiten Sie die Kommentare von Ingo:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}