Ось як використовувати generics, щоб отримати масив саме того типу, який ви шукаєте, зберігаючи безпеку типу (на відміну від інших відповідей, які або повернуть вам 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 . Довідники класу трактуються компілятором як екземпляри 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));
}