Ось як використовувати 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));
}