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