Ошибка при создании массива generics

411
03 февраля 2017, 04:32

Предположим, есть такой код:

List<Integer>[] lists = new ArrayList<Integer>[10];

Компилятор не пропустит такого объявления. Почему?

P.S. Я знаю, что создавать подобный массив в реальном коде не стоит (лучше использовать коллекции) и знаю способы как обойти эту ошибку.

Answer 1

В документации сказано, что создание массивов из объектов параметризованного типа запрещено для избежания подобной ситуации:

//Предположим, что такое объявление было бы возможно
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[])o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
//присвоение не вызывает ошибок
oa[1] = li;
//Ошибка в run-time: ClassCastException.
String s = lsa[1].get(0);

В этой ситуации код бы скомпилировался, но "сломался" при запуске.

Однако допускается использование wildcard <?> с явным приведением к String в последней строке:

List<?>[] lsa = new List<?>[10];
Object o = lsa;
Object[] oa = (Object[])o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
//Корректно
oa[1] = li;
//Ошибка в run-time, но приведение было явным
String s = (String)lsa[1].get(0);

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

Да, в первой строке можно написать так:

List<String>[] lsa = new List[10];

И тогда код скомпилируется и "свалится" с ClassCastException в последней строке. Однако тут нужно учитывать, что в этой записи в левой части используется параметризованный тип, а в правой - raw type. Такая запись не является рекомендуемой к использованию (кроме ситуаций, когда это действительно необходимо). В частности, потому что допускает некорректную ситуацию:

List<Integer> ints = Arrays.asList(1);
List<String>[] lsa = new List[] { ints };
String s = lsa[0].get(0);

Код скомпилируется и, как это было уже не раз в ответе, приведёт к ClassCastException в последней строке.

Answer 2

Судя по документации

You cannot create arrays of parameterized types. For example, the following code does not compile:

List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error

Так что придется делать список списков.

Можно попробовать сделать так:

ArrayList<Integer>[] group = (ArrayList<Integer>[])new ArrayList[4];

Но, ИМХО, это костыльно.

Answer 3

Проблема в том, что массивы в Java ковариантны (т.е. если класс B - наследник A, то B[] - наследник A[] и мутабельны (значение элементов массива можно менять). Т.е. можно написать:

String[] strings = new String[1];
Object[] objects = strings; // ковариантность
objects[0] = new Object();  // мутабельность, эта строка компилируется,
                            // но при запуске выдаст исключение.

При попытке использовать элемент strings, например strings[0].length(), произошла бы ошибка, т.к. на самом деле в массиве лежит Object. Такие ошибки сложно исправлять, потому что заполнение и использование массива могут находиться в разных частях программы. Поэтому, при присвоении значения элементу, происходит проверка соответствия типа, и может быть выброшено исключение java.lang.ArrayStoreException.

Дженерики в java реализованы с помощью type erasure, информация о конкретных значениях параметров типов известна только на этапе компиляции (non-reifiable types), а во время выполнения типы List<String> и List<Integer> "стираются" до простого List. Поэтому проверить, что элементу массива List<String>[] присваивается именно List<String> невозможно, и создание массивов с non-reifiable элементами запрещено.

Дженерики по-умолчанию инвариантны (List<String> не является наследником List<Object> и наоборот), поэтому можно использовать коллекции обобщенных типов.

READ ALSO
Как использовать Graphics в Java? [требует правки]

Как использовать Graphics в Java? [требует правки]

Как использовать Graphics в Java?

330
Исключения при работе с сервлетом [требует правки]

Исключения при работе с сервлетом [требует правки]

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

304
Как спрятать массив пикселей в текущем изображении [требует правки]

Как спрятать массив пикселей в текущем изображении [требует правки]

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

360
Android ошибка при поиске фрагмента

Android ошибка при поиске фрагмента

Здравствуйте, недавно начал изучать Android, вылезла такая ошибка:

375