Як створити загальний масив на Java?


1090

Через реалізацію дженерики 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;
}

Але я дійсно не розумію, що відбувається.


14
Вам справді потрібно використовувати масив? А як щодо використання колекції?
мат б

12
Так, я також думаю, що колекції є більш елегантними для цієї проблеми. Але це для класового завдання, і вони потрібні :(
tatsuhirosatou

3
Я не розумію, навіщо мені тут потрібне відображення. Граматика Яви дивна: як новий java.util.HashMap <String, String> [10] недійсний. новий java.util.HashMap <long, long> (10) недійсний. new long [] [10] недійсний, new long [10] [] є дійсним. Цей матеріал змушує написати програму, яка може написати програму java, складніше, ніж це виглядає.
бронзовий чоловік

Відповіді:


703

Я маю задати питання натомість: це 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: вона була реалізована за допомогою стирання, тому "загальні" класи не знають, з яким аргументом типу вони були створені під час виконання, і тому не можуть надати тип- безпека, якщо не реалізований явний механізм (перевірка типу).


7
Що було б найкращим варіантом? Мені потрібно отримувати елементи з цього масиву досить часто (в циклі). Тож колекція, мабуть, повільніше, але хто з цих двох найшвидший?
user1111929

3
І якщо загальний тип обмежений, резервний масив повинен мати тип обмеження.
Мордехай

5
@AaronDigulla Просто для уточнення, що це не призначення, а ініціалізація локальної змінної. Ви не можете коментувати вираз / вираз.
kennytm

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, а не з масивами, якщо ви використовуєте дженерики. Звичайно, іноді у вас немає вибору, але використовувати рамки колекцій набагато надійніше.


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 це генерує попередження: "Неперевірена
передача

61

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

5
Це марно, це лише складний спосіб написання нового рядка [...]. Але те, що дійсно потрібно, - це щось на зразок публічного статичного <T> T [] newArray (int size) {...}, і цього просто не існує в java noir, чи можна це імітувати відображенням - причина полягає в тому, що інформація про те, як загальний тип інстанціюється, недоступний під час виконання.
Інго

4
@Ingo Про що ти говориш? Мій код можна використовувати для створення масиву будь-якого типу.
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);
}

Мені довелося роздивитися, але так, другий аргумент "length" до Arrays#copyOf()- не залежить від довжини масиву, що подається як перший аргумент. Це розумно, хоча він і платить вартість дзвінків, Math#min()і System#arrayCopy()жоден з них не є строго необхідним, щоб виконати цю роботу. docs.oracle.com/javase/7/docs/api/java/util / ...
SEH

8
Це не працює, якщо Eє змінною типу. Варагги створює масив стирання, Eколи Eє змінною типу, завдяки чому він не сильно відрізняється від (E[])new Object[n]. Перегляньте http://ideone.com/T8xF91 . Це аж ніяк не безпечніший тип, ніж будь-яка інша відповідь.
Radiodef

1
@Radiodef - рішення, очевидно, є безпечним для типу під час компіляції. зауважте, що стирання не є точно частиною мовної специфікації; специфікація написана обережно, щоб ми могли мати повну модифікацію в майбутньому - і тоді це рішення буде чудово працювати і під час виконання, на відміну від інших рішень.
ЧжунЮ

@Radiodef - Суперечливо, чи заборона створення загального масиву є хорошою ідеєю. незважаючи на те, мова все ж залишає задній простір - vararg вимагає створення загального масиву. Це так добре, як якщо б мова дозволила new E[]. Проблема, яку ви показали у своєму прикладі, є загальною проблемою стирання, не властивою лише цьому питанню та цій відповіді.
ЧжунЮ

2
@Radiodef - Є деякі відмінності. Правильність цього рішення перевіряє компілятор; вона не покладається на людські міркування про вимушений акторський склад. Різниця не є істотною для цієї конкретної проблеми. Деякі люди просто хочуть трохи пофантазувати, ось і все. Якщо хтось введений в оману формулюванням ОП, це з’ясовується вашими коментарями та моїми.
ZhongYu

33

Щоб продовжити до декількох вимірів, просто додайте []«s і параметри вимірювання в newInstance()( Tє параметром типу, clsє 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 ми можемо зробити своєрідне створення загального масиву, використовуючи лямбда або метод посилань. Це схоже на рефлексивний підхід (який проходить а 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 , використовуючи анонімні класи , але це більш громіздким.


Вам не потрібен спеціальний інтерфейс, як ArraySupplierдля цього, ви можете оголосити конструктор як GenSet(Supplier<E[]> supplier) { ...і викликати його тим же рядком, що і у вас.
Лій

4
@Lii Щоб бути таким, як мій приклад, було б IntFunction<E[]>, але так, це правда.
Radiodef

11

Це висвітлено у розділі 5 (Загальна інформація) Ефективної Java, 2-е видання , пункт 25 ... Віддайте перевагу спискам для масивів

Ваш код буде працювати, хоча він генеруватиме неперевірене попередження (яке ви можете придушити за допомогою наступної примітки:

@SuppressWarnings({"unchecked"})

Однак, мабуть, було б краще використовувати Список замість масиву.

На сайті проекту OpenJDK є цікаве обговорення цієї помилки / функції .


8

Аргумент класу не потрібно передавати конструктору. Спробуйте це.

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

Діагностика Java працює, перевіряючи типи під час компіляції та вставляючи відповідні касти, але стираючи типи у компільованих файлах. Це робить загальні бібліотеки зручними для використання за кодом, який не розуміє дженерики (що було обдуманим дизайнерським рішенням), але це означає, що ви не можете нормально з'ясувати, який тип знаходиться під час виконання.

Громадський Stack(Class<T> clazz,int capacity)конструктор вимагає , щоб передати об'єкт Class під час виконання, в якому інформація кошти класу є доступні під час виконання в коді , який потребує в цьому. А Class<T>форма означає, що компілятор перевірить, що об’єкт Class, який ви передаєте, є саме об’єктом Class для типу T. Не підкласом T, не надкласом T, а саме T.

Тоді це означає, що ви можете створити об’єкт масиву відповідного типу у своєму конструкторі, а це означає, що тип об’єктів, які ви зберігаєте у вашій колекції, буде перевіряти їхні типи в точці, коли вони додаються до колекції.


6

Привіт, хоча нитка мертва, я хотів би звернути вашу увагу на це:

Generics використовується для перевірки типу під час компіляції:

  • Тому метою є перевірка того, що приходить - те, що вам потрібно.
  • Що ви повертаєте - те, що потрібно споживачеві.
  • Перевір це:

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

Не хвилюйтеся про набір попереджень, коли ви пишете загальний клас. Турбуйтеся, коли ви ним користуєтеся.


6

Що з цим рішенням?

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

Це працює і виглядає занадто просто, щоб бути правдою. Чи є якийсь недолік?


3
Акуратний, але працює лише в тому випадку, якщо ви називаєте це "вручну", тобто передайте елементи окремо. Якщо ви не можете створити новий екземпляр T[], тоді ви не можете програмно скласти а 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;
}

Він перетворює список будь-якого виду об’єктів у масив одного типу.


Так, ви повернете null, що не очікуваний порожній масив. Це найкраще, що можна зробити, але не ідеально.
Кевін Кокс

Це також може вийти з ладу, якщо у 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...
}

Тепер в основному ми можемо створити масив так:

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.


4

Прикладом є використання відображення Java для створення масиву. Робити це зазвичай не рекомендується, оскільки це не безпечно. Натомість, ви повинні просто скористатися внутрішнім списком і взагалі уникати масиву.


13
Другий приклад ( з використанням Array.newInstance ()) це насправді типізованим. Це можливо, тому що тип T об'єкта Class повинен відповідати T масиву. В основному, ви змушуєте вас надати інформацію про те, що час виконання Java відкидає генеричні дані.
Йоахім Зауер


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 є частиною Весни.


3

Насправді, простіший спосіб зробити це - створити масив об’єктів і передати його потрібному типу, як у наведеному нижче прикладі:

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

де SIZEє константа і Tє ідентифікатором типу


1

Запропонований іншими людьми примусовий акторський склад для мене не працював, викидаючи виняток із незаконного кастингу.

Однак цей неявний виступ спрацював чудово:

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

де Item - це клас, який я визначив, що містить член:

private K value;

Таким чином ви отримуєте масив типу K (якщо елемент має лише значення) або будь-який загальний тип, який ви хочете визначити у класі Item.


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() - це правильний тип (навіть якщо ми знаємо).

Цей стиль є трохи некрасивим, але іноді може бути найменш поганим рішенням для створення загальних типів, які дійсно повинні знати свій тип компонентів під час виконання з будь-якої причини (створення масивів або створення примірників типу їх компонентів тощо).


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

Мені потрібно було щось подібне в якомусь коді, над яким я працював, і саме це я зробив, щоб змусити його працювати. Поки проблем немає.


0

Ви можете створити масив Object і передати його скрізь E. Так, це не дуже чистий спосіб зробити це, але він повинен принаймні працювати.


"Ми шукаємо довгі відповіді, які дають певні пояснення та контекст. Не просто дайте однорядну відповідь; поясніть, чому ваша відповідь правильна, в ідеалі - цитатами. Відповіді без пояснень можуть бути видалені".
gparyani

Але НЕ буде працювати в деяких випадках, наприклад, якщо ваш загальний клас хоче реалізувати Порівнюваний інтерфейс.
RamPrasadBismil

Ласкаво просимо до семи років тому.
Есько

1
Це не спрацює, якщо ви спробуєте повернути масив із загального коду на негенеричний абонент. Буде головомірне класовевизначення.
підключення

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

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 / ...
Paul Bellora

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

Редагувати: Можливо, альтернативним способом створення такого масиву, якщо потрібний вам розмір був відомий і малий, було б просто ввести необхідну кількість "null" s в команду zeroArray?

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


Ні, це не працює. Varargs створює стирання, Tколи Tє змінною типу, тобто zeroArrayповертає an 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, а не масиву якогось параметризованого типу.
Сотіріос Деліманоліс

Він виконує те саме завдання, але не вимагає від вас класу в класі, що полегшує користувальницьку колекцію.
Крабова туманність

Яке завдання ? Це буквально інше завдання: масив параматеризованого типу проти масиву параметра загального типу.
Сотіріос Деліманоліс

Це дозволяє створити масив із загального типу? Початкова проблема полягала в ініціалізації масиву з використанням загального типу, який за допомогою мого методу дозволяє вам робити без необхідності натискання користувачем класу або надання неперевіреної помилки, наприклад, спроби віддати об’єкт до рядка. Як холод, я не найкращий у тому, що я роблю, і я не ходив до школи з програмування, але я думаю, що все-таки заслуговую на невеликий внесок, а не про те, щоб сказати якусь іншу дитину в Інтернеті.
Крабова туманність

Я згоден з Сотіросом. Є два способи придумати відповідь. Або це відповідь на інше питання, або це спроба узагальнити питання. Обидва помиляються / не допомагають. Люди, які шукають вказівки щодо того, як реалізувати клас "загальний масив", перестали б читати, читаючи заголовок питання. І коли вони знайдуть питання з 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];
 }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.