Вот как использовать обобщенные элементы для получения массива именно того типа, который вы ищете при сохранении безопасности типов (в отличие от других ответов, которые либо вернут вам Objectмассив, либо приведут к предупреждениям во время компиляции):
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];
}
}
Он компилируется без предупреждений, и, как вы можете видеть main, для любого типа, который вы объявляете экземпляром GenSetas, вы можете назначить aмассив этого типа и назначить элемент из aпеременной этого типа, то есть массив и значения в массиве имеют правильный тип.
Он работает с использованием литералов класса в качестве токенов типа времени выполнения, как обсуждалось в Учебниках Java . Литералы класса обрабатываются компилятором как экземпляры java.lang.Class. Чтобы использовать один, просто следуйте за именем класса с .class. Таким образом, String.classдействует как Classобъект, представляющий класс String. Это также работает для интерфейсов, перечислений, любых размерных массивов (например String[].class), примитивов (например int.class) и ключевого слова void(например void.class).
Classсам по себе является общим (объявляется как Class<T>, где Tобозначает тип, который представляет Classобъект), означая, что тип String.classявляется Class<String>.
Таким образом, всякий раз, когда вы вызываете конструктор для GenSet, вы передаете литерал класса для первого аргумента, представляющего массив GenSetобъявленного типа экземпляра (например, String[].classдля GenSet<String>). Обратите внимание, что вы не сможете получить массив примитивов, поскольку примитивы нельзя использовать для переменных типа.
Внутри конструктора вызов метода castвозвращает переданный Objectаргумент в класс, представленный Classобъектом, для которого был вызван метод. Вызов статического метода newInstancein java.lang.reflect.Arrayвозвращает в виде Objectмассива типа, представленного Classобъектом, переданным в качестве первого аргумента, и длины, указанной в intкачестве второго аргумента. Вызов метода getComponentTypeвозвращает Classобъект, представляющий тип компонента массива, представленного Classобъектом, для которого был вызван метод (например, String.classдля String[].class, nullесли Classобъект не представляет массив).
Это последнее предложение не совсем точно. Вызов String[].class.getComponentType()возвращает Classобъект, представляющий класс String, но его тип - Class<?>нет Class<String>, поэтому вы не можете сделать что-то вроде следующего.
String foo = String[].class.getComponentType().cast("bar"); // won't compile
То же самое касается каждого метода, Classкоторый возвращает Classобъект.
Что касается комментария Йоахима Сауэра к этому ответу (у меня недостаточно репутации, чтобы самому комментировать его), то пример использования приведения к T[]приведёт к предупреждению, потому что компилятор не может гарантировать безопасность типов в этом случае.
Изменить комментарии Инго:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}