Вот как использовать обобщенные элементы для получения массива именно того типа, который вы ищете при сохранении безопасности типов (в отличие от других ответов, которые либо вернут вам 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
, для любого типа, который вы объявляете экземпляром GenSet
as, вы можете назначить 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
объектом, для которого был вызван метод. Вызов статического метода newInstance
in 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));
}