Создание массива общих коллекций - PullRequest
20 голосов
/ 13 декабря 2011

На самом деле, вопрос должен быть

Creating an array of generic anything.

Почему компилятор не может позаботиться об этом?

Следующее будет помечено как ошибка - невозможно создать универсальный массив.

List<MyDTO>[] dtoLists = {new ArrayList<MyDTO>(), anExistingDtoList};

Чтобы преодолеть это, мне нужно

List<MyDTO>[] dtoLists = (List<MyDTO>[])Array.newInstance(ArrayList.class, 2);
dtoLists[0] = new ArrayList<MyDTO>();
dtoLists[1] = anExistingDtoList;

Так почему же компилятор не может преобразовать первый случай во второй?

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

С какими технологическими / логическими барьерами столкнутся разработчики компиляторов, которые не позволят им реализовать это?

Является ли проблема чисто философской, касающейся языковой ортогональности? Если да, то как такое поведение может нарушить ортогональность языка?

Это вопрос сложности? Объясните сложность.

Я надеюсь, что ответы на мой вопрос дадут мне лучшее представление о поведении java-компилятора, когда оно касается генериков.

Примечание: давай перестать быть счастливым. Ответы Массив общего списка не отвечай на мой вопрос. Почему компиляторы не могут самопроизвольно выполнить преобразование?

Ответы [ 3 ]

8 голосов
/ 13 декабря 2011

На самом деле Java создает универсальный массив для varargs, поэтому вы можете сделать

List<MyDTO>[] dtoLists = array(new ArrayList<MyDTO>(), anExistingDtoList);

@SafeVarargs
static <E> E[] array(E... array)
{
    return array;
}

Относительно того, почему явное создание универсального массива запрещено, оно имеет отношение к стиранию типа. (То же самое беспокойство существует в указанном выше растворе, но подавляется @SafeVarargs), однако это спорно; Есть разные способы справиться с проблемой, вероятно, достаточно предупреждения компилятора. Но они решили полностью запретить это, вероятно, потому что массивы больше не важны, так как у нас есть общие коллекции

3 голосов
/ 13 декабря 2011

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

1 голос
/ 16 мая 2015

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

См. 10,5.Исключение хранилища массивов :

Для массива с типом A[], где A - ссылочный тип, присваивание компоненту массива проверяется при запускевремя, чтобы убедиться, что присваиваемое значение присваивается компоненту.

Если тип присваиваемого значения не совместим с присваиванием с типом компонента, выдается ArrayStoreException.

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

A List<MyDTO>[] не сгенерирует, если мы добавим в него какой-то другой тип List, поэтомуон не ведет себя как массив.Обратите внимание на последнее предложение из цитаты: «Вот почему выражение создания массива с типом элемента, не относящегося к числу reifiable, запрещено.» Это причина, по которой она указана.(И, к сведению, это рассуждение всегда существовало , поэтому оно присутствовало, когда вопрос был опубликован в 2011 году.)

Мы все еще можем сделать это:

@SuppressWarnings({"unchecked","rawtypes"})
List<MyDTO>[] dtoLists = new List[] {
    new ArrayList<MyDTO>(), anExistingDtoList
};

Или это:

@SuppressWarnings("unchecked")
List<MyDTO>[] dtoLists = (List<MyDTO>[]) new List<?>[] {
    new ArrayList<MyDTO>(), anExistingDtoList
};

(Помимо статической проверки типов аргументов, вещь varargs эквивалентна: она создает List[] и подавляет предупреждения .)

Теперь, конечно, спецификацию можно изменить на что-то вроде "Если тип присваиваемого значения не совместим с присваиванием с необработанным типом компонентатипа ... ", но какой в ​​этом смысл?Это спасло бы горстку персонажей в некоторых необычных ситуациях, но в противном случае подавило бы предупреждения для тех, кто не понимает последствий.

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

Например, с учетом следующего объявления:

// (declaring our own because Arrays.fill is defined as
// void fill(Object[], Object)
// so the next examples would more obviously pass)
static <T> void fill(T[] arr, T elem) {
    Arrays.fill(arr, elem);
}

Знаете ли вы, что это компилируется?

// throws ArrayStoreException
fill(new String[1], new Integer(0));

И это тоже компилируется:

// doesn't throw ArrayStoreException
fill(dtoLists, new ArrayList<Float>());

До Java 8 мы могли бы сделать эти вызовы на fill неудачными, сделав следующее объявление:

static <T, U extends T> void fill(T[] arr, U elem) {...}

Но это была только проблема с выводом типа, и теперь он работает «правильно», слепо вставляя List<Float> в List<MyDTO>[].

Это называется загрязнением кучи .Это может привести к тому, что ClassCastException будет брошено через некоторое время, вероятно, куда-то, совершенно не связанное с действиями, которые фактически вызвали проблему.Загрязнение кучи с помощью универсального контейнера, такого как List, требует более очевидных небезопасных действий, таких как использование raw types , но здесь мы можем вызвать загрязнение кучи неявно и без каких-либо предупреждений.

Универсальные массивы (и действительно, массивы в целом) дают нам статическую проверку только в самых простых обстоятельствах.

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

...