Oto jak używać ogólnych, aby uzyskać tablicę dokładnie tego, czego szukasz, przy jednoczesnym zachowaniu bezpieczeństwa typu (w przeciwieństwie do innych odpowiedzi, które albo zwrócą Objecttablicę, albo spowodują pojawienie się ostrzeżeń w czasie kompilacji):
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];
}
}
To się kompiluje bez ostrzeżeń i jak widać w main, dla dowolnego typu deklarowanego jako instancję GenSet, można przypisać ado tablicy tego typu i można przypisać element ado zmiennej tego typu, co oznacza, że tablica a wartości w tablicy są poprawnego typu.
Działa przy użyciu literałów klasowych jako tokenów typu wykonawczego, jak omówiono w samouczkach Java . Literały klas są traktowane przez kompilator jako instancje java.lang.Class. Aby użyć jednego, po prostu podążaj za nazwą klasy za pomocą .class. String.classDziała więc jako Classobiekt reprezentujący klasę String. Działa to również w przypadku interfejsów, wyliczeń, tablic dowolnego wymiaru (np. String[].class), Prymitywów (np. int.class) I słowa kluczowego void(tj void.class.).
Classsam jest ogólny (zadeklarowany jako Class<T>, gdzie Toznacza typ reprezentowany przez Classobiekt), co oznacza, że typem String.classjest Class<String>.
Tak więc, za każdym razem, gdy wywołujesz konstruktor GenSet, przekazujesz literał klasy dla pierwszego argumentu reprezentującego tablicę GenSetzadeklarowanego typu instancji (np. String[].classDla GenSet<String>). Zauważ, że nie będziesz w stanie uzyskać tablicy prymitywów, ponieważ prymitywów nie można używać do zmiennych typu.
Wywołanie metody wewnątrz konstruktora castzwraca przekazany Objectargument rzutowany do klasy reprezentowanej przez Classobiekt, na którym wywołano metodę. Wywołanie metody statycznej newInstancew java.lang.reflect.Arrayzwraca jako Objecttablicę typu reprezentowanego przez Classobiekt przekazany jako pierwszy argument i o długości określonej przez intprzekazany jako drugi argument. Wywołanie metody getComponentTypezwracającej ClassPrzedmiotem reprezentującym typ komponentu tablicy reprezentowane przez Classobiekt na którym sposób został nazwany (na przykład String.classprzez String[].class, nulljeśli Classobiekt nie stanowią macierz).
To ostatnie zdanie nie jest do końca dokładne. Wywołanie String[].class.getComponentType()zwraca Classobiekt reprezentujący klasę String, ale jego typ Class<?>nie Class<String>jest, dlatego nie można zrobić czegoś takiego jak poniżej.
String foo = String[].class.getComponentType().cast("bar"); // won't compile
To samo dotyczy każdej metody, Classktóra zwraca Classobiekt.
W odniesieniu do komentarza Joachima Sauera do tej odpowiedzi (nie mam wystarczającej reputacji, aby skomentować ją osobiście), przykład użycia cast do T[]spowoduje ostrzeżenie, ponieważ kompilator nie może zagwarantować bezpieczeństwa typu w tym przypadku.
Edytuj w odniesieniu do komentarzy Ingo:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}