Почему мы можем использовать массив с общей ссылкой - PullRequest
6 голосов
/ 27 марта 2012

При ответе на вопрос об этом здесь: https://stackoverflow.com/a/9872630/82609

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

Это немного похоже на принуждение людей использовать List<String> = new ArrayList(); вместо List<String> = new ArrayList<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);

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

Ответы [ 2 ]

4 голосов
/ 27 марта 2012

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

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

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

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

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

1 голос
/ 27 марта 2012

Цитирование из великого Effective Java Second Edition стр. 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)

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

...