Компиляторы могут самопроизвольно выполнять преобразование, они просто указаны не для , потому что универсальные массивы не могут вести себя как неуниверсальные массивы.
См. 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 , но здесь мы можем вызвать загрязнение кучи неявно и без каких-либо предупреждений.
Универсальные массивы (и действительно, массивы в целом) дают нам статическую проверку только в самых простых обстоятельствах.
Таким образом, очевидно, что разработчики языка думали, что лучше просто не позволять им, и программисты, которые понимают проблемы, которые они представляют, могутподавить предупреждения и обойти ограничение.