Почему мы можем использовать массив с общей ссылкой

Отвечая на вопрос об этом здесь: qaru.site/questions/911735/...

Я попытался сделать следующее:

Comparator<string>[] comparators = new Comparator[] {...};
</string>

Это работает! Но следующее:

Comparator<string>[] comparators = new Comparator<string>[] {...};
</string></string>

По соответствующему вопросу я сделал предположение:

Я предполагаю, что изначально контракт с массивом может быть чем-то например:

Если вы создаете массив типа X, вы НИКОГДА НИКОГДА не сможете поставить   что-то в нем, что IS-NOT-AN X. Если вы попробуете, вы получите ArrayStoreException

Таким образом, создание массивов с созданием дженериков приведет к правило вроде:

Если вы создадите массив типа X, вы НИКОГДА НИКОГДА не сможете   поместите что-нибудь, что НЕ-НЕ-А X. Если вы попробуете, вы получите   ArrayStoreException. Но вы можете добавлять объекты X и X из-за стирания стилей!

Но думая об этом, было бы действительно проблемой:

Comparator<string>[] comparators = new Comparator<string>[] {...};
</string></string>

Я действительно не понимаю, почему это невозможно, поскольку использование такой вещи:

  • Проверьте классы, вставленные во время выполнения
  • Проверьте тип классов, вставленный во время компиляции

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

Мне просто интересно, знает ли кто-нибудь причину этого выбора?

Это немного похоже на принуждение людей использовать List<string> = new ArrayList();</string> вместо использования List<string> = new ArrayList<string>();</string> </string>

dimitrisli вы дали хороший пример из знаменитой книги Джошуа Блоха. Как вы объяснили это, опасно использовать как общие массивы + ковариацию, так и привести к ClassCastException, в то время как мы ожидаем ArrayStoreException из массива с использованием ковариации.

Но, пожалуйста, обратите внимание, что все еще законно и приводит к тому же:

List<string>[] stringLists = new List[1];
List<integer> intList = Arrays.asList(42);
Object[] objects = stringLists;
objects[0] = intList;
String s = stringLists[0].get(0);
</integer></string>

Однако при компиляции он выдает предупреждение о непроверенных предупреждениях, и, как вы отметили, ClassCastException во время выполнения.

2 ответа

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

Как вы отметили, стирание означает, что общие параметры недоступны во время выполнения, поэтому типы проверяются во время компиляции (будь то List или ваш Comparator<string>[]</string>). Критически это основано на общем параметре переменной.

Массивы, с другой стороны, проверяют типы своих аргументов во время выполнения, когда они вставлены, поэтому они могут бросать ArrayStoreException, если они неправильно используются (как правило, из-за злоупотребления их ковариацией). Поэтому массивы должны иметь возможность выполнять обе проверки вашей маркерной точки внутри страны, и, конечно же, они не могут проверить общий параметр во время выполнения. Следовательно, нет смысла создавать экземпляр общего массива, так как массив должен полностью игнорировать общий параметр, который в лучшем случае вводит в заблуждение.

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

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


Цитата из большого Эффективное второе издание Java стр. 120:

Почему создание общего массива является незаконным - не будет компилироваться!

List<string>[] stringLists = new List<string>[1]; // (1)
List<integer> intList = Arrays.asList(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
</integer></string></string>

Давайте сделаем вид, что строка 1, которая создает общий массив, является законной. Строка 2 создает и инициализирует a List, содержащий один элемент. Строка 3 хранит List в переменную массива объектов, которая является законной, поскольку массивы являются ковариантными. Строка 4 сохраняет List в единственный элемент Массив объектов, который преуспевает, поскольку генерические средства реализуются путем стирания: Тип выполнения экземпляра List - это просто List, а тип выполнения a List<string>[]</string> экземпляр List[], поэтому это назначение не генерирует ArrayStoreException. Теперь были в беде. Weve сохранил List экземпляр в массив, который объявлен для хранения только экземпляров List. В строка 5, мы извлекаем единственный элемент из единственного списка в этом массиве. Компилятор автоматически отбрасывает извлеченный элемент в String, но его Integer, поэтому мы получаем a ClassCastException во время выполнения. Чтобы этого не произошло, строка 1 (который создает общий массив) генерирует ошибку времени компиляции.

licensed under cc by-sa 3.0 with attribution.