В ответ я должен задать вопрос: у вас GenSet "проверено" или "непроверено"? Что это значит?
Проверено: строгая типизация. GenSet явно знает, какой тип объектов он содержит (т. Е. его конструктор был явно вызван с Class<E> аргументом, и методы будут генерировать исключение, когда им передаются аргументы, отличающиеся от типа E. Смотрите Collections.checkedCollection.
-> в этом случае вам следует написать:
publicclassGenSet<E> {
private E[] a;
publicGenSet(Class<E> c, int s) { // Use Array native method to create array // of a type only known at run time @SuppressWarnings("unchecked") final E[] a = (E[]) Array.newInstance(c, s); this.a = a; }
E get(int i) { return a[i]; } }
Непроверено: слабая типизация. Проверка типа фактически не выполняется ни для одного из объектов, передаваемых в качестве аргумента.
Обратите внимание, что типом компонента массива должно быть стирание параметра type:
publicclassGenSet<E extendsFoo> { // E has an upper bound of Foo
private Foo[] a; // E erases to Foo, so use Foo[]
publicGenSet(int s) { a = newFoo[s]; }
... }
Все это является результатом известной и преднамеренной слабости универсальных классов в Java: это было реализовано с использованием erasure, поэтому "универсальные" классы не знают, с каким аргументом типа они были созданы во время выполнения, и, следовательно, не могут обеспечить безопасность типов, если не реализован какой-либо явный механизм (проверка типов).
Ответ 2
Вы можете сделать это:
E[] arr = (E[])newObject[INITIAL_ARRAY_LENGTH];
Это один из предложенных способов реализации универсальной коллекции в эффективной Java; Пункт 26. Нет ошибок типа, нет необходимости повторно приводить массив. Однако это вызывает предупреждение, потому что это потенциально опасно, и его следует использовать с осторожностью. Как подробно описано в комментариях, это Object[] теперь маскируется под наш E[] тип и может вызвать непредвиденные ошибки или ClassCastException s при небезопасном использовании.
Как правило, такое поведение безопасно до тех пор, пока приведенный массив используется внутри (например, для резервного копирования структуры данных), а не возвращается или предоставляется клиентскому коду. Если вам нужно вернуть массив универсального типа в другой код, упомянутый вами класс reflection Array - правильный путь.
Стоит упомянуть, что везде, где это возможно, вам будет гораздо приятнее работать с Lists, а не с массивами, если вы используете generics. Конечно, иногда у вас нет выбора, но использование платформы collections Framework гораздо надежнее.
Ответ 3
Вот как использовать generics, чтобы получить массив именно того типа, который вы ищете, сохраняя при этом безопасность типов (в отличие от других ответов, которые либо вернут вам Object массив, либо приведут к предупреждениям во время компиляции):
import java.lang.reflect.Array;
publicclassGenSet<E> { private E[] a;
publicGenSet(Class<E[]> type, int length) { a = type.cast(Array.newInstance(type.getComponentType(), length)); }
Который компилируется без предупреждений, и, как вы можете видеть в 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>.
Итак, всякий раз, когда вы вызываете конструктор for GenSet, вы передаете литерал класса для первого аргумента, представляющего массив объявленного типа GenSet экземпляра (например, String[].class for 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>, поэтому вы не можете сделать что-то вроде следующего.
То же самое касается каждого метода в Class который возвращает Class объект.
Что касается комментария Иоахима Зауэра к этому ответу (у меня недостаточно репутации, чтобы прокомментировать это самому), пример с использованием приведения к T[] приведет к предупреждению, потому что компилятор не может гарантировать безопасность типов в этом случае.