Я не понимаю связи между универсальными файлами и массивами.
Я могу создать ссылку на массив с универсальным типом:
private E[] elements; //GOOD
Но не удается создать объект array с универсальным типом:
elements = newE[10]; //ERROR
Но это работает:
elements = (E[]) newObject[10]; //GOOD
Переведено автоматически
Ответ 1
Не следует путать массивы и универсальные типы. Они плохо сочетаются друг с другом. Существуют различия в том, как массивы и универсальные типы обеспечивают проверку типов. Мы говорим, что массивы являются материализованными, а дженерики - нет. В результате вы видите эти различия при работе с массивами и дженериками.
Массивы ковариантны, дженерики - нет:
Что это значит? Вы, должно быть, уже знаете, что допустимо следующее назначение:
Object[] arr = newString[10];
В принципе, Object[] это супертип String[], потому что Object это супертип String. Это не относится к дженерикам. Итак, следующее объявление недопустимо и не будет компилироваться:
List<Object> list = newArrayList<String>(); // Will not compile.
Причина в том, что универсальные массивы инвариантны.
Принудительная проверка типа:
В Java были введены универсальные типы для обеспечения более строгой проверки типов во время компиляции. Таким образом, универсальные типы не содержат никакой информации о типе во время выполнения из-за удаления типа. Итак, a List<String> имеет статический тип List<String>, но динамический тип List.
Однако массивы содержат информацию о типе компонента во время выполнения. Во время выполнения массивы используют проверку хранилища массивов, чтобы проверить, вставляете ли вы элементы, совместимые с фактическим типом массива. Итак, следующий код:
скомпилируется нормально, но завершится сбоем во время выполнения в результате ArrayStoreCheck . С generics это невозможно, поскольку компилятор попытается предотвратить исключение во время выполнения, предоставив проверку во время компиляции, избегая создания ссылки, подобной этой, как показано выше.
Итак, в чем проблема с созданием универсального массива?
Создание массива, тип компонента которого является либо параметром типа, конкретным параметризованным типом, либо ограниченным параметризованным типом с подстановочным знаком, небезопасно для типов.
Рассмотрим приведенный ниже код:
public <T> T[] getArray(int size) { T[] arr = newT[size]; // Suppose this was allowed for the time being. return arr; }
Поскольку тип T неизвестен во время выполнения, созданный массив на самом деле является Object[]. Таким образом, приведенный выше метод во время выполнения будет выглядеть следующим образом:
Теперь предположим, что вы вызываете этот метод как:
Integer[] arr = getArray(10);
Вот в чем проблема. Вы только что присвоили Object[] ссылке на Integer[]. Приведенный выше код будет скомпилирован нормально, но завершится ошибкой во время выполнения.
Вот почему создание универсального массива запрещено.
Почему приведение типов new Object[10] к E[] работает?
Теперь ваше последнее сомнение, почему приведенный ниже код работает:
E[] elements = (E[]) newObject[10];
Приведенный выше код имеет те же последствия, что и объясненный выше. Если вы заметили, компилятор выдаст вам предупреждение о непроверенном приведении, поскольку вы выполняете приведение к массиву неизвестного типа компонента. Это означает, что приведение может завершиться ошибкой во время выполнения. Например, если у вас есть этот код в приведенном выше методе:
это приведет к сбою во время выполнения с ClassCastException . Итак, нет, этот способ не будет работать всегда.
Как насчет создания массива типа List<String>[]?
Проблема та же. Из-за удаления типа a List<String>[] является ничем иным, как a List[]. Итак, если бы создание таких массивов было разрешено, давайте посмотрим, что могло бы произойти.:
List<String>[] strlistarr = newList<String>[10]; // Won't compile. but just consider it Object[] objarr = strlistarr; // this will be fine objarr[0] = newArrayList<Integer>(); // This should fail but succeeds.
Теперь проверка ArrayStoreCheck в приведенном выше случае завершится успешно во время выполнения, хотя это должно было вызвать исключение ArrayStoreException. Это потому, что оба List<String>[] и List<Integer>[] компилируются в List[] во время выполнения.
Итак, можем ли мы создать массив неограниченных параметризованных типов с подстановочными знаками?
ДА. Причина в том, что a List<?> является повторяемым типом. И это имеет смысл, поскольку с типом вообще не связано. Таким образом, в результате удаления типа терять нечего. Таким образом, создание массива такого типа абсолютно типобезопасно.
Оба приведенных выше случая хороши, потому что List<?> это супертип для всех экземпляров универсального типа List<E>. Таким образом, он не выдаст исключение ArrayStoreException во время выполнения. То же самое происходит и с массивом необработанных типов. Поскольку необработанные типы также являются воспроизводимыми типами, вы можете создать массив List[].
Итак, получается, что вы можете создать массив только из воспроизводимых типов, но не из невоспроизводимых типов. Обратите внимание, что во всех вышеперечисленных случаях объявление массива выполняется нормально, проблемы возникают при создании массива с помощью new оператора. Но нет смысла объявлять массив этих ссылочных типов, поскольку они не могут указывать ни на что, кроме null (игнорируя неограниченные типы).
public <E> E[] getArray(Class<E> clazz, int size) { @SuppressWarnings("unchecked") E[] arr = (E[]) Array.newInstance(clazz, size);
return arr; }
Приведение к типу необходимо, потому что этот метод возвращает Object. Но вы можете быть уверены, что это безопасное приведение. Таким образом, вы даже можете использовать @SuppressWarnings для этой переменной.
Ответ 2
Вот реализация LinkedList<T>#toArray(T[]):
public <T> T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); inti=0; Object[] result = a; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item;
if (a.length > size) a[size] = null;
return a; }
Короче говоря, вы могли создавать универсальные массивы только через Array.newInstance(Class, int) где int - размер массива.
Ответ 3
Проблема в том, что во время выполнения общий тип стирается, поэтому new E[10] было бы эквивалентно new Object[10].
Это было бы опасно, потому что в массив можно было бы поместить данные, отличные от E типа. Вот почему вам нужно явно указать, какой тип вы хотите, либо
создаем массив объектов и преобразуем его в E[] массив, или