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ą Object
tablicę, 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ć a
do tablicy tego typu i można przypisać element a
do 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.class
Działa więc jako Class
obiekt 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
.).
Class
sam jest ogólny (zadeklarowany jako Class<T>
, gdzie T
oznacza typ reprezentowany przez Class
obiekt), co oznacza, że typem String.class
jest Class<String>
.
Tak więc, za każdym razem, gdy wywołujesz konstruktor GenSet
, przekazujesz literał klasy dla pierwszego argumentu reprezentującego tablicę GenSet
zadeklarowanego typu instancji (np. String[].class
Dla 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 cast
zwraca przekazany Object
argument rzutowany do klasy reprezentowanej przez Class
obiekt, na którym wywołano metodę. Wywołanie metody statycznej newInstance
w java.lang.reflect.Array
zwraca jako Object
tablicę typu reprezentowanego przez Class
obiekt przekazany jako pierwszy argument i o długości określonej przez int
przekazany jako drugi argument. Wywołanie metody getComponentType
zwracającej Class
Przedmiotem reprezentującym typ komponentu tablicy reprezentowane przez Class
obiekt na którym sposób został nazwany (na przykład String.class
przez String[].class
, null
jeśli Class
obiekt nie stanowią macierz).
To ostatnie zdanie nie jest do końca dokładne. Wywołanie String[].class.getComponentType()
zwraca Class
obiekt 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, Class
która zwraca Class
obiekt.
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));
}