Как создать универсальный массив в Java?

Из-за реализации обобщений Java, вы не можете иметь такой код:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

Как я могу реализовать это при сохранении безопасности типов?

Я видел решение на форумах Java, которое выглядит следующим образом:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

Но я действительно не понимаю, что происходит.

9.02.2009 17:30:44
Вам действительно нужно использовать массив здесь? Как насчет использования коллекции?
matt b 9.02.2009 18:34:09
Да, я также считаю, что коллекции более элегантны для этой проблемы. Но это для задания класса, и они требуются :(
tatsuhirosatou 9.02.2009 19:47:52
Я не понимаю, почему мне нужно отражать здесь. Ява грамматика странная: как новый java.util.HashMap <String, String> [10] недопустимо. новый java.util.HashMap <long, long> (10) недопустим. new long [] [10] недопустим, new long [10] [] допустим. Эти вещи заставляют писать программу, которая может написать программу Java, сложнее, чем кажется.
bronze man 30.06.2017 02:46:14
30 ОТВЕТОВ
РЕШЕНИЕ

Я должен задать вопрос в ответ: ваш GenSet"проверен" или "не проверен"? Что это обозначает?

  • Проверено : сильная типизация . GenSetточно знает, какой тип объектов он содержит (т.е. его конструктор был явно вызван с Class<E>аргументом, и методы будут генерировать исключение, когда им передаются аргументы, не являющиеся типом E. См Collections.checkedCollection.

    -> в этом случае вы должны написать:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(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];
        }
    }
  • Не проверено : слабая типизация . Проверка типов на самом деле не выполняется ни для одного из объектов, переданных в качестве аргумента.

    -> в этом случае, вы должны написать

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }

    Обратите внимание, что тип компонента массива должен быть стиранием параметра типа:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }

Все это происходит из-за известной и преднамеренной слабости обобщений в Java: он был реализован с использованием стирания, поэтому «универсальные» классы не знают, с каким аргументом типа они были созданы во время выполнения, и поэтому не могут предоставить тип-тип. безопасность, если не реализован какой-либо явный механизм (проверка типов).

695
6.04.2015 04:19:53
Что будет с точки зрения производительности лучшим вариантом? Мне нужно получать элементы из этого массива довольно часто (внутри цикла). Таким образом, коллекция, вероятно, медленнее, но какая из этих двух самая быстрая?
user1111929 8.09.2012 03:52:51
И если универсальный тип ограничен, резервный массив должен иметь ограничивающий тип.
Mordechai 8.04.2013 05:59:03
@AaronDigulla Просто чтобы уточнить, что это не присваивание, а инициализация локальной переменной. Вы не можете комментировать выражение / заявление.
kennytm 26.09.2013 12:17:32
@ Varkhan Есть ли способ изменить размеры этих массивов из реализации класса. Например, если я хочу изменить размер после переполнения, как ArrayList. Я посмотрел реализацию ArrayList у них Object[] EMPTY_ELEMENTDATA = {}для хранения. Могу ли я использовать этот механизм для изменения размера, не зная тип с использованием обобщений?
JourneyMan 28.08.2014 17:53:26
Для тех, кто хочет создать метод с универсальным типом (именно это я и искал), используйте это:public void <T> T[] newArray(Class<T> type, int length) { ... }
Daniel Kvist 13.03.2015 14:44:27

В примере используется отражение Java для создания массива. Делать это обычно не рекомендуется, так как это небезопасно. Вместо этого вам нужно просто использовать внутренний List и вообще избегать массива.

4
9.02.2009 17:33:58
Второй пример ( с использованием Array.newInstance ()) это на самом деле типизированным. Это возможно, потому что тип T объекта Class должен совпадать с T массива. Это в основном вынуждает вас предоставлять информацию, которую среда выполнения Java отбрасывает для обобщений.
Joachim Sauer 9.02.2009 22:41:38

Вы можете создать массив объектов и привести его к E везде. Да, это не очень чистый способ сделать это, но он должен по крайней мере работать.

0
9.02.2009 17:46:55
«Мы ищем длинные ответы, которые дают некоторое объяснение и контекст. Не просто дайте однострочный ответ; объясните, почему ваш ответ правильный, в идеале с цитатами. Ответы без объяснений могут быть удалены».
gparyani 16.09.2014 15:46:28
Но в некоторых случаях это не сработает, например, если ваш универсальный класс хочет реализовать Comparable интерфейс.
RamPrasadBismil 21.04.2016 08:55:58
Добро пожаловать семь лет назад, я полагаю.
Esko 26.04.2016 19:03:24
Это не будет работать, если вы попытаетесь вернуть массив из универсального кода неуниверсальному вызывающему объекту. Там будет головокружительный урок.
plugwash 12.07.2017 12:52:04

Это описано в главе 5 (Общие положения) Effective Java, 2-е издание , пункт 25 ... Предпочитать списки массивам

Ваш код будет работать, хотя он будет генерировать непроверенное предупреждение (которое вы можете отключить с помощью следующей аннотации:

@SuppressWarnings({"unchecked"})

Однако, вероятно, было бы лучше использовать List вместо Array.

На сайте проекта OpenJDK есть интересное обсуждение этой ошибки / функции .

11
29.08.2016 16:56:59

Обобщения Java работают, проверяя типы во время компиляции и вставляя соответствующие приведения, но стирая типы в скомпилированных файлах. Это делает универсальные библиотеки пригодными для использования кодом, который не понимает универсальные (что было преднамеренным дизайнерским решением), но это означает, что вы обычно не можете узнать, какой тип во время выполнения.

Общественный Stack(Class<T> clazz,int capacity)конструктор требует , чтобы передать объект Class во время выполнения, в котором информация средства класса является доступны во время выполнения в коде , который нуждается в этом. И Class<T>форма означает, что компилятор проверит, что передаваемый вами объект Class является именно объектом Class для типа T. Не подклассом T, не суперклассом T, но именно T.

Затем это означает, что вы можете создать в своем конструкторе объект массива соответствующего типа, а это означает, что тип объектов, хранящихся в вашей коллекции, будет проверяться по типам в момент их добавления в коллекцию.

7
11.02.2009 10:07:35

Ты можешь сделать это:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

Это один из предложенных способов реализации универсальной коллекции в эффективной Java; Пункт 26 . Нет ошибок типа, нет необходимости повторно приводить массив. Однако это вызывает предупреждение, потому что это потенциально опасно, и его следует использовать с осторожностью. Как подробно описано в комментариях, Object[]теперь это маскируется под наш E[]тип и может вызывать неожиданные ошибки или ClassCastExceptions, если используется небезопасно.

Как правило, такое поведение безопасно, если массив приведения используется внутренне (например, для поддержки структуры данных), а не возвращается или подвергается воздействию клиентского кода. Если вам нужно вернуть массив универсального типа в другой код, упомянутый Arrayвами класс отражения - правильный путь.


Стоит отметить, что везде, где это возможно, вы будете гораздо счастливее работать с Lists, а не с массивами, если используете дженерики. Конечно, иногда у вас нет выбора, но использование фреймворка коллекций гораздо надежнее.

214
29.11.2018 23:24:32
Это не будет работать, если массив обрабатывается как типизированный массив любого типа, например, String[] s=b;в приведенном выше test()методе. Это потому, что массив E не совсем, это Object []. Это важно, если вы хотите, например List<String>[]- вы не можете использовать Object[]для этого, вы должны иметь List[]специально. Вот почему вам нужно использовать отраженный класс <?> Для создания массива.
Lawrence Dol 11.10.2010 16:09:20
Угловой случай / проблема заключается в том, если вы хотите сделать, например, public E[] toArray() { return (E[])internalArray.clone(); }когда internalArrayнапечатано как E[], и, следовательно, на самом деле является Object[]. Это завершается с ошибкой во время выполнения с исключением приведения типа, потому что Object[]нельзя присвоить массиву любого типа E, который оказывается.
Lawrence Dol 10.08.2011 20:04:03
По сути, этот подход будет работать до тех пор, пока вы не вернете массив, не передадите его и не сохраните его в каком-либо месте вне класса, для которого требуется массив определенного типа. Пока ты в классе, ты в порядке, потому что E стерта. Это «опасно», потому что если вы попытаетесь вернуть его или что-то, вы не получите предупреждения, что это небезопасно. Но если вы осторожны, то это работает.
newacct 23.09.2011 22:07:46
Это довольно безопасно. В E[] b = (E[])new Object[1];вы можете ясно видеть , что единственная ссылка на созданный массив является bи что тип bявляется E[]. Поэтому нет никакой опасности, что вы случайно получите доступ к одному и тому же массиву через другую переменную другого типа. Если вместо этого у вас есть, Object[] a = new Object[1]; E[]b = (E[])a; то вы должны быть параноиком о том, как вы используете a.
Aaron McDaid 21.01.2012 19:53:30
По крайней мере, в Java 1.6 это генерирует предупреждение: «Не проверено приведение от Object [] к T []»
Quantum7 24.03.2012 00:42:33

Вот как использовать обобщенные элементы для получения массива именно того типа, который вы ищете, при сохранении безопасности типов (в отличие от других ответов, которые либо вернут вам 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, для любого типа, который вы объявляете экземпляром GenSetas, вы можете назначить 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объектом, для которого был вызван метод. Вызов статического метода newInstancein 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));
}
61
23.05.2017 12:34:45
Это бесполезно, это только сложный способ написать новую строку [...]. Но что действительно нужно, так это что-то вроде public static <T> T [] newArray (int size) {...}, и это просто не существует в java noir, может ли это быть смоделировано с отражением - причина в том, что информация о том, как экземпляр общего типа недоступен во время выполнения.
Ingo 21.03.2011 10:11:10
@ Инго О чем ты говоришь? Мой код может быть использован для создания массива любого типа.
gdejohn 23.03.2011 12:34:03
@Charlatan: Конечно, но может и новый []. Вопрос в том, кто знает тип и когда. Поэтому, если у вас есть общий тип, вы не можете.
Ingo 23.03.2011 12:48:24
Я не сомневаюсь в этом. Дело в том, что вы не получите объект Class во время выполнения для универсального типа X.
Ingo 23.03.2011 12:58:46
Почти. Я признаю, что это больше, чем то, что может быть достигнуто с помощью нового []. На практике это почти всегда делает работу. Однако все еще невозможно, например, написать класс контейнера, параметризованный E, который имеет метод E [] toArray () и который действительно возвращает истинный массив E []. Ваш код может быть применен только тогда, когда в коллекции есть хотя бы один E-объект. Таким образом, общее решение невозможно.
Ingo 23.03.2011 13:31:04

попробуй это.

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}
0
13.02.2011 20:37:34
Я не могу запустить ваш код, откуда ваш Elementкласс?
user8389458 21.02.2018 00:15:00

Привет, хотя поток мертв, я хотел бы обратить ваше внимание на это:

Generics используется для проверки типов во время компиляции:

  • Поэтому цель состоит в том, чтобы проверить, что вам нужно.
  • То, что вы возвращаете, - это то, что нужно потребителю.
  • Проверь это:

введите описание изображения здесь

Не беспокойтесь о предупреждениях при вводе типов при написании универсального класса. Беспокойство, когда вы используете его.

6
8.12.2012 10:56:23

Это единственный ответ, который является безопасным типом

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}
40
6.04.2015 04:11:51
Мне пришлось искать это, но да, второй аргумент «length» Arrays#copyOf()не зависит от длины массива, предоставленного в качестве первого аргумента. Это умно, хотя и оплачивает стоимость звонков, Math#min()и System#arrayCopy()ни один из них не является строго необходимым для выполнения этой работы. docs.oracle.com/javase/7/docs/api/java/util/...
seh 4.10.2012 19:53:30
Это не работает, если Eпеременная типа. Varargs создает массив стирания Eкогда Eявляется переменной типа, что мало чем отличается от него (E[])new Object[n]. Пожалуйста, смотрите http://ideone.com/T8xF91 . Это ни в коем случае не более безопасный тип, чем любой другой ответ.
Radiodef 6.04.2015 04:13:12
@Radiodef - во время компиляции решение является доказуемо безопасным по типу. обратите внимание, что стирание не является частью языковой спецификации; спецификация написана тщательно, чтобы в будущем мы могли полностью реализовать ее - и тогда это решение будет отлично работать и во время выполнения, в отличие от других решений.
ZhongYu 18.05.2015 18:35:57
@Radiodef - это спорно ли запрет на создание общего массива является хорошей идеей. независимо от того, язык оставляет черный ход - vararg требует создания универсального массива. Это так же хорошо, как если бы язык позволил new E[]. Проблема, которую вы показали в своем примере, является общей проблемой стирания, не уникальной для этого вопроса и этого ответа.
ZhongYu 18.05.2015 18:38:20
@Radiodef - Есть некоторые различия. Корректность этого решения проверяется компилятором; это не полагается на человеческое рассуждение насильственного броска. Разница не является существенной для этой конкретной проблемы. Некоторым людям просто нравится быть немного модными, вот и все. Если кто-то введен в заблуждение формулировкой ФП, это разъясняется вашими и моими комментариями.
ZhongYu 18.05.2015 19:19:28

Простой, хотя и грязный, обходной путь к этому заключался бы в том, чтобы поместить второй класс-держатель в ваш основной класс и использовать его для хранения ваших данных.

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}
0
5.04.2012 00:10:45
Это на самом деле не работает. new Holder<Thing>[10]является созданием универсального массива.
Radiodef 10.03.2014 19:43:54

Я сделал этот фрагмент кода, чтобы рефлексивно создать экземпляр класса, который передается для простой автоматизированной утилиты тестирования.

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

Обратите внимание на этот сегмент:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

для инициации массива, где Array.newInstance (класс массива, размер массива) . Класс может быть как примитивным (int.class), так и объектом (Integer.class).

BeanUtils является частью Spring.

3
31.08.2012 10:39:28

Посмотрите также на этот код:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

Он преобразует список объектов любого типа в массив того же типа.

5
8.08.2013 23:32:27
Да, вы возвращаете ноль, который не является ожидаемым пустым массивом. Это лучшее, что вы можете сделать, но не идеально.
Kevin Cox 7.02.2014 14:49:38
Это также может привести к сбою, если Listобъект содержит более одного типа, например toArray(Arrays.asList("abc", new Object())), выбрасывает ArrayStoreException.
Radiodef 6.04.2015 04:36:23
Я использовал урезанную версию этого; Первое, что я смог использовать, это сработало, хотя, по общему признанию, я не пробовал некоторые из более сложных решений. Чтобы избежать forцикла и других я использовал, Arrays.fill(res, obj);так как я хотел одно и то же значение для каждого индекса.
bbarker 10.08.2015 01:04:57

Чтобы расширить на другие измерения, просто добавьте []параметры s и измерения к newInstance()( Tэто параметр типа, clsa Class<T>, d1через d5целые числа):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

Смотрите Array.newInstance()подробности.

33
12.01.2014 18:55:29
+1 Были вопросы о создании многомерных массивов, которые были закрыты в качестве дураков из этого поста, но ни одного ответа конкретно не было адресовано этому.
Paul Bellora 15.08.2013 13:52:53
@JordanC Возможно; хотя по духу он такой же, как stackoverflow.com/a/5671304/616460 ; Я подумаю о лучшем способе справиться завтра. Я сонный.
Jason C 12.11.2014 05:19:23

Может быть, не имеет отношения к этому вопросу, но пока я получаю " generic array creation" ошибку для использования

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

Я нахожу следующие работы (и работал для меня) с @SuppressWarnings({"unchecked"}):

 Tuple<Long, String>[] tupleArray = new Tuple[10];
0
21.08.2013 16:11:35
Да, это не совсем связано, но коренится в тех же проблемах (стирание, ковариация массива). Вот пример поста о создании массивов параметризованных типов: stackoverflow.com/questions/9542076/…
Paul Bellora 21.08.2013 16:23:07

Принудительное приведение в действие, предложенное другими людьми, не работало для меня, исключая незаконное приведение.

Однако это неявное приведение работало нормально:

Item<K>[] array = new Item[SIZE];

где Item является классом I, который содержит член:

private K value;

Таким образом, вы получите массив типа K (если элемент имеет только значение) или любой универсальный тип, который вы хотите определить в классе Item.

1
14.09.2013 21:26:33

В Java 8 мы можем сделать своего рода создание универсального массива, используя лямбду или ссылку на метод. Это похоже на рефлексивный подход (который проходит a Class), но здесь мы не используем рефлексию.

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

Например, это используется <A> A[] Stream.toArray(IntFunction<A[]>).

Это также может быть сделано до Java 8 с использованием анонимных классов, но это более громоздко.

13
5.07.2017 01:17:05
Вам действительно не нужен специальный интерфейс, подобный ArraySupplierэтому, вы можете объявить конструктор как GenSet(Supplier<E[]> supplier) { ...и вызвать его с той же строкой, что и у вас.
Lii 27.12.2015 23:49:23
@Lii Чтобы быть таким же, как мой пример, это было бы IntFunction<E[]>, но да, это правда.
Radiodef 28.12.2015 16:16:27

Мне интересно, если этот код создаст эффективный универсальный массив?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

Редактировать: Возможно, альтернативным способом создания такого массива, если требуемый размер был известен и мал, было бы просто ввести необходимое количество «нулевых» в команду zeroArray?

Хотя очевидно, что это не так универсально, как использование кода createArray.

0
9.07.2014 13:36:35
Нет, это не работает. Varargs создает стирание, Tкогда Tявляется переменной типа, то есть zeroArrayвозвращает Object[]. См. Http://ideone.com/T8xF91 .
Radiodef 6.04.2015 04:03:56

Вы можете использовать приведение:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}
0
15.09.2014 17:23:32
Если вы собираетесь это предложить, вам действительно нужно объяснить его ограничения. Никогда не подвергайте aвне класса!
Radiodef 6.04.2015 04:00:53
private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}
-1
3.06.2015 06:16:21
Вы всегда должны добавлять пояснения к своему коду и объяснять, почему он решает оригинальный опубликованный вопрос.
mjuarez 3.06.2015 06:11:29

На самом деле более простой способ сделать это - создать массив объектов и привести его к нужному типу, как показано в следующем примере:

T[] array = (T[])new Object[SIZE];

где SIZEконстанта и Tидентификатор типа

3
12.06.2015 09:53:54

Никто другой не ответил на вопрос о том, что происходит в приведенном вами примере.

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

Как говорили другие, дженерики «стираются» во время компиляции. Таким образом, во время выполнения экземпляр универсального не знает, каков его тип компонента. Причиной этого является историческая причина: Sun хотела добавить универсальные шаблоны, не нарушая существующий интерфейс (как исходный, так и двоичный).

Массивы с другой стороны делать знают их компоненты типа во время выполнения.

Этот пример решает проблему, когда код, вызывающий конструктор (который знает тип), передает параметр, сообщающий классу требуемый тип.

Таким образом, приложение будет создавать класс с чем-то вроде

Stack<foo> = new Stack<foo>(foo.class,50)

и конструктор теперь знает (во время выполнения), что представляет собой тип компонента, и может использовать эту информацию для построения массива через API отражения.

Array.newInstance(clazz, capacity);

Наконец, у нас есть приведение типа, потому что компилятор не может знать, что возвращаемый массив Array#newInstance()является правильным типом (даже если мы знаем).

Этот стиль немного уродлив, но иногда он может быть наименее плохим решением для создания универсальных типов, которым по какой-либо причине необходимо знать их тип компонента во время выполнения (создание массивов или создание экземпляров их типа компонентов и т. Д.).

1
18.10.2015 14:54:58

Как насчет этого решения?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

Это работает и выглядит слишком просто, чтобы быть правдой. Есть ли недостаток?

6
21.02.2016 01:28:56
Аккуратно, но работает, только если вы называете это «вручную», т.е. передаете элементы индивидуально. Если вы не можете создать новый экземпляр T[], вы не можете программно создать a T[] elemsдля передачи в функцию. И если бы вы могли, вам не понадобилась бы эта функция.
orlade 29.08.2016 01:41:42

Я нашел способ обойти эту проблему.

В строке ниже выдается ошибка создания общего массива

List<Person>[] personLists=new ArrayList<Person>()[10];

Однако, если я инкапсулирую List<Person>в отдельный класс, это работает.

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

Вы можете выставлять людей в классе PersonList через геттер. Строка ниже даст вам массив, который есть List<Person>в каждом элементе. Другими словами массив List<Person>.

PersonList[] personLists=new PersonList[10];

Мне нужно что-то подобное в некотором коде, над которым я работал, и это то, что я сделал, чтобы заставить его работать. Пока проблем нет.

1
19.10.2016 12:57:33

Я нашел быстрый и простой способ, который работает для меня. Обратите внимание, что я использовал это только на Java JDK 8. Я не знаю, будет ли он работать с предыдущими версиями.

Хотя мы не можем создать экземпляр универсального массива с определенным параметром типа, мы можем передать уже созданный массив в конструктор универсального класса.

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

Теперь в main мы можем создать массив следующим образом:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

Для большей гибкости с вашими массивами вы можете использовать связанный список, например. ArrayList и другие методы, найденные в классе Java.util.ArrayList.

5
9.11.2016 20:43:28

На самом деле я нашел довольно уникальное решение, чтобы обойти невозможность инициировать универсальный массив. То, что вам нужно сделать, это создать класс, который принимает переменную общего типа T следующим образом:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

а затем в вашем классе массива просто начните так:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

запуск new Generic Invoker[]вызывает проблему с непроверенным, но на самом деле не должно быть никаких проблем.

Чтобы получить из массива, вы должны вызвать массив [i] .variable следующим образом:

public T get(int index){
    return array[index].variable;
}

Остальное, например изменение размера массива, можно сделать с помощью Arrays.copyOf () следующим образом:

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

И функция добавления может быть добавлена ​​так:

public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}
0
28.06.2017 19:59:53
Речь шла о создании массива типа параметра общего типа T, а не массива какого-либо параметризованного типа.
Sotirios Delimanolis 29.06.2017 15:14:46
Тем не менее, он выполняет ту же задачу и не требует добавления класса, что упрощает использование пользовательской коллекции.
Crab Nebula 29.06.2017 23:50:43
Какое задание ? Это буквально другая задача: массив параматеризованного типа по сравнению с массивом параметра универсального типа.
Sotirios Delimanolis 30.06.2017 00:16:17
Это позволяет вам создать массив из универсального типа? Первоначальная проблема заключалась в инициализации массива с использованием универсального типа, который с помощью моего метода позволяет вам делать это без необходимости толкать пользователя в классе или выдавать неконтролируемую ошибку, такую ​​как попытка привести Object к String. Как холод, я не лучший в том, что я делаю, и я не ходил в школу по программированию, но я думаю, что я все еще заслуживаю небольшого вклада, вместо того, чтобы быть рассказанным другим ребенком в Интернете.
Crab Nebula 30.06.2017 23:53:48
Я согласен с Сотиросом. Есть два способа придумать ответ. Либо это ответ на другой вопрос, либо попытка обобщить вопрос. Оба не правы / не помогают. Люди, которые ищут руководство по реализации класса «универсального массива», перестанут читать, когда прочитают заголовок вопроса. И когда они находят Q с 30 ответами, очень маловероятно, что они прокрутят до конца и прочитают ответ с нулевым голосованием от новичка SO.
Stephen C 11.07.2017 23:00:28

Вам не нужно передавать аргумент Class в конструктор. Попробуй это.

public class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

а также

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

результат:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]
7
22.12.2018 11:27:23

Передача списка значений ...

public <T> T[] array(T... values) {
    return values;
}
3
15.09.2017 10:19:41

Создание общего массива запрещено в Java, но вы можете сделать это как

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
-1
19.04.2018 13:45:59

Если вы действительно хотите обернуть универсальный массив фиксированного размера, у вас будет метод для добавления данных в этот массив, поэтому вы можете правильно инициализировать массив, выполняя что-то вроде этого:

import java.lang.reflect.Array;

class Stack<T> {
    private T[] array = null;
    private final int capacity = 10; // fixed or pass it in the constructor
    private int pos = 0;

    public void push(T value) {
        if (value == null)
            throw new IllegalArgumentException("Stack does not accept nulls");
        if (array == null)
            array = (T[]) Array.newInstance(value.getClass(), capacity);
        // put logic: e.g.
        if(pos == capacity)
             throw new IllegalStateException("push on full stack");
        array[pos++] = value;
    }

    public T pop() throws IllegalStateException {
        if (pos == 0)
            throw new IllegalStateException("pop on empty stack");
        return array[--pos];
    }
}

в этом случае вы используете java.lang.reflect.Array.newInstance для создания массива, и он будет не Object [], а настоящим T []. Вы не должны беспокоиться о том, что он не является окончательным, поскольку он управляется внутри вашего класса. Обратите внимание, что вам нужен ненулевой объект в push (), чтобы можно было использовать используемый тип, поэтому я добавил проверку данных, которые вы отправляете, и сгенерировал там исключение.

Тем не менее, это несколько бессмысленно: вы сохраняете данные с помощью push, и именно сигнатура метода гарантирует, что будут входить только T-элементы. Так что более или менее неважно, что массив это Object [] или T [].

-1
6.11.2019 13:23:32