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

1091

Из-за реализации обобщений 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;
}

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

tatsuhirosatou
источник
14
Вам действительно нужно использовать массив здесь? Как насчет использования коллекции?
Matt B
12
Да, я также думаю, что коллекции более элегантны для этой проблемы. Но это для задания класса, и они требуются :(
tatsuhirosatou
3
Я не понимаю, почему мне нужно отражать здесь. Ява грамматика странная: как новый java.util.HashMap <String, String> [10] недопустимо. новый java.util.HashMap <long, long> (10) недопустим. new long [] [10] недопустим, new long [10] [] допустим. Эти вещи заставляют писать программу, которая может написать программу на Java, сложнее, чем кажется.
бронзовый человечек

Ответы:

704

Я должен задать вопрос в ответ: ваш 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: он был реализован с использованием стирания, поэтому «универсальные» классы не знают, с каким аргументом типа они были созданы во время выполнения, и поэтому не могут предоставить тип-тип. безопасность, если не реализован какой-либо явный механизм (проверка типов).

Varkhan
источник
7
Что будет с точки зрения производительности лучшим вариантом? Мне нужно получать элементы из этого массива довольно часто (внутри цикла). Таким образом, коллекция, вероятно, медленнее, но какой из этих двух самый быстрый?
user1111929
3
И если универсальный тип ограничен, резервный массив должен иметь ограничивающий тип.
Мордехай
5
@AaronDigulla Просто чтобы уточнить, что это не присваивание, а инициализация локальной переменной. Вы не можете комментировать выражение / заявление.
Кеннитм,
1
@ Varkhan Есть ли способ изменить размеры этих массивов из реализации класса. Например, если я хочу изменить размер после переполнения, как ArrayList. Я посмотрел реализацию ArrayList у них Object[] EMPTY_ELEMENTDATA = {}для хранения. Могу ли я использовать этот механизм для изменения размера, не зная тип с использованием обобщений?
JourneyMan
2
Для тех, кто хочет создать метод с универсальным типом (именно это я и искал), используйте это:public void <T> T[] newArray(Class<T> type, int length) { ... }
Даниэль Квист
225

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

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

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

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


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

dimo414
источник
47
Это не будет работать, если массив обрабатывается как типизированный массив любого типа, например, String[] s=b;в приведенном выше test()методе. Это потому, что массив E не совсем, это Object []. Это важно, если вы хотите, например List<String>[]- вы не можете использовать Object[]для этого, вы должны иметь List[]специально. Вот почему вам нужно использовать отраженный класс <?> Для создания массива.
Лоуренс Дол
8
Угловой случай / проблема заключается в том, если вы хотите сделать, например, public E[] toArray() { return (E[])internalArray.clone(); }когда internalArrayнапечатано как E[], и, следовательно, на самом деле является Object[]. Это завершается с ошибкой во время выполнения с исключением приведения типа, потому что Object[]невозможно присвоить массиву любого типа E.
Лоуренс Дол
17
По сути, этот подход будет работать до тех пор, пока вы не вернете массив, не передадите его и не сохраните его в каком-либо месте вне класса, для которого требуется массив определенного типа. Пока ты в классе, ты в порядке, потому что E стерта. Это «опасно», потому что если вы попытаетесь вернуть его или что-то еще, вы не получите предупреждения, что это небезопасно. Но если вы осторожны, то это работает.
newacct
3
Это довольно безопасно. В E[] b = (E[])new Object[1];вы можете ясно видеть , что единственная ссылка на созданный массив является bи что тип bявляется E[]. Поэтому нет никакой опасности, что вы случайно получите доступ к одному и тому же массиву через другую переменную другого типа. Если вместо этого у вас есть, Object[] a = new Object[1]; E[]b = (E[])a; то вы должны быть параноиком о том, как вы используете a.
Аарон МакДейд
5
По крайней мере, в Java 1.6 это генерирует предупреждение: «Непроверенное приведение от Object [] к T []»
Quantum7
61

Вот как использовать обобщенные элементы для получения массива именно того типа, который вы ищете при сохранении безопасности типов (в отличие от других ответов, которые либо вернут вам 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));
}
gdejohn
источник
5
Это бесполезно, это только сложный способ написать новую строку [...]. Но что действительно нужно, так это что-то вроде public static <T> T [] newArray (int size) {...}, и это просто не существует в java noir, может ли это быть смоделировано с отражением - причина в том, что информация о том, как экземпляр общего типа недоступен во время выполнения.
Инго
4
@ Инго О чем ты говоришь? Мой код может быть использован для создания массива любого типа.
gdejohn
3
@Charlatan: Конечно, но может и новый []. Вопрос в том, кто знает тип и когда. Поэтому, если у вас есть общий тип, вы не можете.
Инго
2
Я не сомневаюсь в этом. Дело в том, что вы не получите объект Class во время выполнения для универсального типа X.
Ingo
2
Почти. Я признаю, что это больше, чем то, что может быть достигнуто с помощью нового []. На практике это почти всегда делает работу. Однако все еще невозможно, например, написать класс контейнера, параметризованный E, который имеет метод E [] toArray () и который действительно возвращает истинный массив E []. Ваш код может быть применен только тогда, когда в коллекции есть хотя бы один E-объект. Таким образом, общее решение невозможно.
Инго
42

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

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}
irreputable
источник
Мне пришлось искать это, но да, второй аргумент "length" Arrays#copyOf()не зависит от длины массива, предоставленного в качестве первого аргумента. Это умно, хотя и оплачивает стоимость звонков, Math#min()и System#arrayCopy()ни один из них не является строго необходимым для выполнения этой работы. docs.oracle.com/javase/7/docs/api/java/util/...
SEH
8
Это не работает, если Eпеременная типа. Varargs создает массив стирания Eкогда Eявляется переменной типа, что мало чем отличается от него (E[])new Object[n]. Пожалуйста, смотрите http://ideone.com/T8xF91 . Это ни в коем случае не более безопасный тип, чем любой другой ответ.
Radiodef
1
@Radiodef - во время компиляции решение является доказуемо безопасным по типу. обратите внимание, что стирание не является частью языковой спецификации; спецификация написана тщательно, чтобы в будущем мы могли полностью реализовать ее - и тогда это решение будет отлично работать и во время выполнения, в отличие от других решений.
ZhongYu
@Radiodef - это спорно ли запрет на создание общего массива является хорошей идеей. независимо от того, язык оставляет черный ход - vararg требует создания универсального массива. Это так же хорошо, как если бы язык позволил new E[]. Проблема, которую вы показали в своем примере, является общей проблемой стирания, не уникальной для этого вопроса и этого ответа.
ZhongYu
2
@Radiodef - Есть некоторые различия. Корректность этого решения проверяется компилятором; это не полагается на человеческое рассуждение насильственного броска. Разница не значительна для этой конкретной проблемы. Некоторым людям просто нравится быть немного модными, вот и все. Если кто-то введен в заблуждение формулировкой ФП, это разъясняется вашими и моими комментариями.
ZhongYu
33

Чтобы расширить на другие измерения, просто добавьте []'s и параметры измерения к newInstance()( Tявляется параметром типа, clsявляется a 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()подробности.

Джейсон С
источник
4
+1 Были вопросы о создании многомерного массива, которые были закрыты в качестве дураков из этого поста, но ни одного ответа конкретно не было адресовано этому.
Пол Беллора
1
@JordanC Возможно; хотя по духу он такой же, как stackoverflow.com/a/5671304/616460 ; Я подумаю о лучшем способе справиться завтра. Я сонный.
Джейсон C
14

В 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 с использованием анонимных классов, но это более громоздко.

Radiodef
источник
Вам действительно не нужен специальный интерфейс, подобный ArraySupplierэтому, вы можете объявить конструктор как GenSet(Supplier<E[]> supplier) { ...и вызвать его с той же строкой, что и у вас.
Лии
4
@Lii Чтобы быть таким же, как мой пример, это было бы IntFunction<E[]>, но да, это правда.
Radiodef
12

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

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

@SuppressWarnings({"unchecked"})

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

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

Джефф Олсон
источник
8

Вам не нужно передавать аргумент 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]
saka1029
источник
7

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

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

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

Билл Мичелл
источник
6

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

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

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

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

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

puneeth
источник
6

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

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

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

Бенджамин М
источник
3
Аккуратно, но работает, только если вы называете это «вручную», т.е. передаете элементы индивидуально. Если вы не можете создать новый экземпляр T[], вы не можете программно создать a T[] elemsдля передачи в функцию. И если бы ты мог, тебе бы не понадобилась эта функция.
Orlade
5

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

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;
}

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

MatheusJardimB
источник
Да, вы возвращаете ноль, который не является ожидаемым пустым массивом. Это лучшее, что вы можете сделать, но не идеально.
Кевин Кокс
Это также может привести к сбою, если Listобъект имеет более одного типа, например toArray(Arrays.asList("abc", new Object())), выбрасывает ArrayStoreException.
Radiodef
Я использовал урезанную версию этого; Первое, что я смог использовать, это сработало, хотя, по общему признанию, я не пробовал некоторые из более сложных решений. Чтобы избежать forцикла и других я использовал, Arrays.fill(res, obj);так как я хотел одно и то же значение для каждого индекса.
bbarker
5

Я нашел быстрый и простой способ, который работает для меня. Обратите внимание, что я использовал это только на 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.

Nikos
источник
4

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

Ола Бини
источник
13
Второй пример ( с использованием Array.newInstance ()) это на самом деле типизированным. Это возможно, потому что тип T объекта Class должен соответствовать T массива. Это в основном вынуждает вас предоставлять информацию, которую среда выполнения Java отбрасывает для обобщений.
Иоахим Зауэр
4

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

public <T> T[] array(T... values) {
    return values;
}
Родриго Асенсио
источник
3

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

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.

Bobster
источник
3

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

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

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

Педрам Эсмаэли
источник
1

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

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

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

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

private K value;

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

vnportnoy
источник
1

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

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()является правильным типом (даже если мы знаем).

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

plugwash
источник
1

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

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

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];

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

developer747
источник
0

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

Эско
источник
«Мы ищем длинные ответы, которые дают некоторое объяснение и контекст. Не просто дайте однострочный ответ; объясните, почему ваш ответ правильный, в идеале с цитатами. Ответы без объяснений могут быть удалены».
gparyani
Но в некоторых случаях это не сработает, например, если ваш универсальный класс хочет реализовать Comparable интерфейс.
RamPrasadBismil
Добро пожаловать семь лет назад, я полагаю.
Эско
1
Это не будет работать, если вы попытаетесь вернуть массив из универсального кода неуниверсальному вызывающему объекту. Там будет головокружительный урок.
plugwash
0

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

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>();
        }
    }
}
Дэвид Бернард
источник
Я не могу запустить ваш код, откуда ваш Elementкласс?
0

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

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}
StarMonkey
источник
3
Это на самом деле не работает. new Holder<Thing>[10]является созданием универсального массива.
Radiodef
0

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

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

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

 Tuple<Long, String>[] tupleArray = new Tuple[10];
Мохсен Афшин
источник
Да, это не совсем связано, но коренится в тех же проблемах (стирание, ковариация массива). Вот пример поста о создании массивов параметризованных типов: stackoverflow.com/questions/9542076/…
Пол Беллора
0

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

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.

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

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

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

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}
Самир Бензолин
источник
Если вы собираетесь это предложить, вам действительно нужно объяснить его ограничения. Никогда не подвергайте aвне класса!
Radiodef
0

На самом деле я нашел довольно уникальное решение, чтобы обойти невозможность инициировать универсальный массив. То, что вам нужно сделать, это создать класс, который принимает переменную общего типа 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++;
}
Крабовидная туманность
источник
1
Речь шла о создании массива типа параметра общего типа T, а не массива какого-либо параметризованного типа.
Сотириос Делиманолис
Тем не менее, он выполняет ту же задачу и не требует добавления класса, что упрощает использование пользовательской коллекции.
Крабовидная туманность
Какое задание ? Это буквально другая задача: массив параматеризованного типа по сравнению с массивом параметра универсального типа.
Сотириос Делиманолис
Это позволяет вам создать массив из универсального типа? Первоначальная проблема заключалась в инициализации массива с использованием универсального типа, который с помощью моего метода позволяет вам делать это без необходимости толкать пользователя в классе или выдавать непроверенную ошибку, такую ​​как попытка привести Object к String. Как холод, я не лучший в том, что я делаю, и я не ходил в школу по программированию, но я думаю, что я все еще заслуживаю небольшого вклада, вместо того, чтобы быть рассказанным другим ребенком в Интернете.
Крабовидная туманность
Я согласен с Сотиросом. Есть два способа придумать ответ. Либо это ответ на другой вопрос, либо попытка обобщить вопрос. Оба не правы / не помогают. Люди, которые ищут руководство о том, как реализовать класс «универсального массива», перестают читать, когда читают заголовок вопроса. И когда они находят Q с 30 ответами, маловероятно, что они прокрутят до конца и прочитают ответ с нулевым голосованием от новичка SO.
Стивен С
0

Согласно внпортному синтаксису

GenSet<Integer> intSet[] = new GenSet[3];

создает массив нулевых ссылок, которые будут заполнены как

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

который типа безопасен.

Сэм Гинрич
источник
-1
private E a[];
private int size;

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

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

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
Ирфан Уль Хак
источник