Создание массива для хранения общих типов в Java

Предположим, что мне нужно создать массив, в котором хранится ArrayList из целых чисел, а размер массива - 10.

Ниже приведен код:

ArrayList<Integer>[] pl2 = new ArrayList[10]; 

Вопрос 1:

По-моему, более подходящим кодом будет

ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];    

Почему это не работает?

Вопрос 2:

Оба из нижеперечисленных компиляций

  • ArrayList<Integer>[] pl2 = new ArrayList[10];
  • ArrayList[] pl3 = new ArrayList[10];

В чем разница в отношении ссылочного объявления pl2 и pl3?

Ответ 1

Вопрос 1:

В принципе, это запрещено языком Java. Это описано в Спецификация языка Java для генериков.

Когда вы используете

ArrayList<Integer>[] pl2 = new ArrayList[10];    // warning

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

ArrayList wrongRawArrayList = new ArrayList();      // warning
wrongRawArrayList.add("string1");                   // warning 
wrongRawArrayList.add("string2");                   // warning  

pl2[0] = wrongRawArrayList;                         // warning 

но теперь ваш массив, который должен содержать ArrayList of Integer, содержит полностью неправильные ArrayList объектов String.

Вопрос 2:

Как уже было сказано, декларация p12 предоставляет вам проверку времени компиляции и освобождает вас от использования каста при получении элементов из вашего ArrayList.

Немного измененный предыдущий пример:

ArrayList<Integer>[] pl2 = new ArrayList[10];                // warning 

ArrayList<String> wrongArrayList = new ArrayList<String>();  // OK!
wrongArrayList.add("string1");                               // OK! 
wrongArrayList.add("string2");                               // OK!

pl2[0] = wrongArrayList;                                     // ERROR

Теперь, поскольку вы используете generics, это не будет компилироваться. Но если вы используете

ArrayList[] pl2 = new ArrayList[10]; 

вы получите тот же результат, что и в первом примере.

Ответ 2

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

Цитата из Think в Java:

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

List<String>[] ls; 

Это проходит через компилятор без жалобы. И хотя вы не может создать фактический объект массива, который содержит дженерики, вы можете создайте массив негенерированного типа и произведите его:

//: arrays/ArrayOfGenerics.java 
// It is possible to create arrays of generics. 
import java.util.*; 

public class ArrayOfGenerics { 
    @SuppressWarnings("unchecked") 
    public static void main(String[] args) { 
        List<String>[] ls; 
        List[] la = new List[10]; 
        ls = (List<String>[])la; // "Unchecked" warning 
        ls[0] = new ArrayList<String>(); 
        // Compile-time checking produces an error: 
        //! ls[1] = new ArrayList<Integer>(); 

        // The problem: List<String> is a subtype of Object 
        Object[] objects = ls; // So assignment is OK 
        // Compiles and runs without complaint: 
        objects[1] = new ArrayList<Integer>(); 

        // However, if your needs are straightforward it is 
        // possible to create an array of generics, albeit 
        // with an "unchecked" warning: 
        List<BerylliumSphere>[] spheres = 
           (List<BerylliumSphere>[])new List[10]; 
        for(int i = 0; i < spheres.length; i++) 
           spheres[i] = new ArrayList<BerylliumSphere>(); 
    } 
}

Как только у вас есть ссылка на список [], вы увидите, что вы получить некоторую проверку времени компиляции. Проблема в том, что массивы covariant, поэтому List [] также является объектом [], и вы можете использовать это, чтобы назначить ArrayList в ваш массив, без ошибок при либо время компиляции, либо время выполнения.

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

Ответ 3

Массивы являются ковариантными. Это означает, что они сохраняют тип своих элементов во время выполнения. Java-дженериков нет. Они используют стирание типа, чтобы в основном замаскировать неявное литье, которое происходит. Важно это понимать.

Вам нужно использовать Array.newInstance()

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

Подробнее см. здесь

Ответ 4

Сначала начнем с вопроса 2, а затем вернемся к вопросу 1:

Вопрос 2:

<Р →   ArrayList [] pl2 = новый ArrayList [10];   ArrayList [] pl3 = новый ArrayList [10];

В чем отличие от ссылочного объявления p12 и p13 имеет значение?

В pl2 обеспечивается более безопасная безопасность, чем p13.

Если я пишу для pl2:

pl2[0]=new ArrayList<String>();

он даст мне ошибку компилятора, в которой говорится: "Невозможно преобразовать из ArrayList<String> в ArrayList<Integer>"

Таким образом, он обеспечивает безопасность времени компиляции.

Однако, если я пишу для p13

pl3[0]=new ArrayList<String>();
pl3[1]=new ArrayList<Integer>();

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

Вопрос 1:

Это просто способ работы дженериков. Во время инициализации основного массива ArrayList<Integer>[] pl2 = new ArrayList[10] левая сторона ArrayList<Integer>[] pl2 гарантирует безопасность типа только при инициализации объекта ArrayList в позиции индекса:

pl2[0]=new ArrayList<Integer>();

Объявление основного массива правой стороны = new ArrayList[10] просто гарантирует, что позиция индекса будет содержать элементы типа ArrayList. Также ознакомьтесь с концепциями стирания типов в Тип Erasure для получения дополнительной информации.

Ответ 5

Это не работает, потому что общие классы не принадлежат Reifiable Types.

JLS о выражении создания массива:

Это ошибка времени компиляции, если [тип класса] не обозначает тип reifiable type (§4.7). В противном случае тип [type] может называть любой именованный ссылочный тип, даже абстрактный тип класса (§8.1.1.1) или тип интерфейса (§9).

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

Определение Reifiable Types:

Поскольку какая-либо информация о типе стирается во время компиляции, не все типы доступны во время выполнения. Типы, которые полностью доступны во время выполнения, известны как повторяющиеся типы.

Тип воспроизводится тогда и только тогда, когда выполняется одно из следующих условий:

It refers to a non-generic class or interface type declaration.

It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).

It is a raw type (§4.8).

It is a primitive type (§4.2).

It is an array type (§10.1) whose element type is reifiable.

It is a nested type where, for each type T separated by a ".", T itself is reifiable.

For example, if a generic class X<T> has a generic member class Y<U>, then the type X<?>.Y<?> is reifiable because X<?> is reifiable and Y<?> is reifiable. The type X<?>.Y<Object> is not reifiable because Y<Object> is not reifiable.

Ответ 6

Вопрос 1.

Ну, это не правильный синтаксис. Следовательно, это не работает.

Вопрос 2.

ArrayList<Integer>[] pl2 = new ArrayList[10];
ArrayList[] pl3 = new ArrayList[10];

Так как pl2 определяется с общим типом <Integer> во время компиляции, компилятор будет знать, что pl2 разрешено иметь только целые числа, и если вы попытаетесь назначить somthing, кроме целых, вы будете предупреждены, а компиляция завершится с ошибкой.

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

Ответ 7

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

После компиляции из-за стирания типа все они становятся ArrayList[] pl2 = new ArrayList[10], но компилятор предупреждает вас, что это плохо.

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

Ответ 8

 ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];

Значит, вам не нужно делать кастинг при извлечении данных из ArrayList пример в нормальном случае

 ArrayList[] pl2 = new ArrayList[10];
 pl2.put(new Integer(10));
 Integer i = p12.get(0);    // this is wrong
 Integer i = (Integer)p12.get(0);    // this is true with casting

но

 ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];   
 pl2.put(new Integer(10));
 Integer i = p12.get(0);    // this is true no need for casting

Ответ 9

Question1

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

Вопрос 2

 ArrayList<Integer>[] pl2 = new ArrayList[10];

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

Но когда вы говорите

 ArrayList[] pl3 = new ArrayList[10];

это означает, что arraylist может хранить любой тип объекта, например string, integer, пользовательские объекты и т.д.

Ответ 11

Насколько я знаю, на Java нет таких вещей, как generics. В терминах типов ArrayList<Integer> and ArrayList - одни и те же вещи.

Java использует стирание стилей для дженериков. Это означает, что вся информация о типе романа стирается во время компиляции. Итак, ArrayList<Integer> станет ArrayList.

Так что это просто трюк с компиляцией. Я предполагаю, что для избежания любых путаниц или ошибок, которые может сделать программист, они разрешали ArrayList<Integer>[] создавать таким образом: new ArrayList[10].

Итак, ArrayList<Integer>[] и a ArrayList[] - это одно и то же, потому что информация в скобках стирается во время компиляции.