Как создать универсальный массив? [дубликат]
этот вопрос уже есть ответ здесь:
Как создать универсальный массив в Java?
29 ответов
Я не понимаю связи между дженериками и массивами.
Я могу создать ссылку на массив с универсального типа:
private E[] elements; //GOOD
но не удается создать объект массива с помощью generic тип:
elements = new E[10]; //ERROR
но это работает:
elements = (E[]) new Object[10]; //GOOD
4 ответов:
не следует путать массивы и дженерики. Они не очень хорошо ладят друг с другом. Существуют различия в том, как массивы и универсальные типы обеспечивают проверку типа. Мы говорим, что массивы овеществлены, но дженерики-нет. В результате вы видите эти различия, работающие с массивами и универсальными моделями.
массивы ковариантны, дженерики не являются:
что это значит? Вы должны уже знать, что следующее задание действительно:
Object[] arr = new String[10];в основном, Ан
Object[]это супер типString[], потому чтоObjectэто супер типString. Это не относится к дженерикам. Таким образом, следующее объявление недействительно и не будет компилироваться:List<Object> list = new ArrayList<String>(); // Will not compile.причина в том, что дженерики инвариантны.
Принудительная Проверка Типа:
дженерики были введены в Java для обеспечения более сильной проверки типа во время компиляции. Таким образом, универсальные типы не имеют никакой информации о типе во время выполнения из-за стирания типа. Так,
List<String>имеет статический типList<String>но динамический типList.однако, массивы несут с собой информацию о времени выполнения типа компонента. Во время выполнения массивы используют проверку хранилища массивов, чтобы проверить, вставляете ли вы элементы, совместимые с фактическим типом массива. Итак, следующий код:
Object[] arr = new String[10]; arr[0] = new Integer(10);будет компилироваться нормально, но не удастся во время выполнения, в результате ArrayStoreCheck. С дженериками это невозможно, так как компилятор попытается предотвратите исключение времени выполнения, предоставив проверку времени компиляции, избегая создания ссылки, как показано выше.
Итак, в чем проблема с созданием универсального массива?
Создание массива, тип компонента которого либо a параметр типа, a конкретный параметризованный тип или ограниченный подстановочный знак параметризованный тип, составляет тип-небезопасная.
рассмотрим код как ниже:
public <T> T[] getArray(int size) { T[] arr = new T[size]; // Suppose this was allowed for the time being. return arr; }так как тип
Tне известно во время выполнения, массив, созданный на самом делеObject[]. Таким образом, приведенный выше метод во время выполнения будет выглядеть так:предположим, что вы вызываете этот метод, как:public Object[] getArray(int size) { Object[] arr = new Object[size]; return arr; }Integer[] arr = getArray(10);вот в чем проблема. Вы только что назначили
Object[]для справкиInteger[]. Приведенный выше код будет компилироваться нормально, но не будет работать во время выполнения.вот почему создание универсального массива запрещенный.
почему typecasting
new Object[10]доE[]работает?теперь ваше последнее сомнение, почему ниже код работает:
E[] elements = (E[]) new Object[10];приведенный выше код имеет те же последствия, что и объясненные выше. Если вы заметили, компилятор будет давать вам Unchecked Cast Warning там, как вы typecasting в массив неизвестного типа компонента. Это означает, что приведение может завершиться неудачей во время выполнения. Например, если у вас есть этот код в приведенном выше метод:
public <T> T[] getArray(int size) { T[] arr = (T[])new Object[size]; return arr; }и вы называете вызвать его так:
String[] arr = getArray(10);это не удастся во время выполнения с ClassCastException. Так что, никакой этот способ не будет работать всегда.
как насчет создания массива типа
List<String>[]?проблема та же. Из-за стирания типа, а
List<String>[]ничегоList[]. Итак, если бы создание таких массивов было разрешено, давайте посмотрим, что может произойти:List<String>[] strlistarr = new List<String>[10]; // Won't compile. but just consider it Object[] objarr = strlistarr; // this will be fine objarr[0] = new ArrayList<Integer>(); // This should fail but succeeds.теперь ArrayStoreCheck в выше case будет успешным во время выполнения, хотя это должно было вызвать исключение ArrayStoreException. Это потому, что оба
List<String>[]иList<Integer>[]компилируются вList[]во время выполнения.Итак, мы можем создать массив неограниченных подстановочных параметризованных типов?
да. Причина в том, что
List<?>является reifiable типом. И это имеет смысл, так как нет никакого типа, связанного вообще. Так что нет ничего, чтобы потерять в результате стирания типа. Таким образом, это совершенно типобезопасно для создания массив такого типа.List<?>[] listArr = new List<?>[10]; listArr[0] = new ArrayList<String>(); // Fine. listArr[1] = new ArrayList<Integer>(); // Fineоба вышеуказанных случая в порядке, потому что
List<?>это супер тип всех экземпляров универсального типаList<E>. Таким образом, он не будет выдавать ArrayStoreException во время выполнения. То же самое происходит и с массивом необработанных типов. Поскольку необработанные типы также являются reifiable типами, вы можете создать массивList[].таким образом, вы можете создать только массив reifiable типов, но не не-reifiable типов. Отметим, что во всех вышеперечисленных случаях декларирование массив в порядке, это создание массива с
newоператор, который дает вопросы. Но нет смысла объявлять массив этих ссылочных типов, поскольку они не могут указывать ни на что, кромеnull(игнорируя неограниченные типы).есть ли обходной путь для
E[]?Да, вы можете создать массив с помощью
Array#newInstance()способ:public <E> E[] getArray(Class<E> clazz, int size) { @SuppressWarnings("unchecked") E[] arr = (E[]) Array.newInstance(clazz, size); return arr; }Typecast необходим, потому что этот метод возвращает
Object. Но вы можете быть уверены, что это безопасно бросить. Таким образом, вы даже можете использовать @SuppressWarnings для этой переменной.
проблема в том, что во время выполнения общий тип стирается так
new E[10]будет эквивалентноnew Object[10].Это было бы опасно, потому что можно было бы положить в массив другие данные, чем
Eтип. Вот почему вам нужно явно сказать, что тип, который вы хотите, либо
- создание массива объектов и приведение его к
E[]массив, или- использование массив.newInstance (Class componentType, int length) to создайте реальный экземпляр массива типа, переданного в
componentTypeargiment.
вот реализация
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); int i = 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- это размер массива.
проверил :
public Constructor(Class<E> c, int length) { elements = (E[]) Array.newInstance(c, length); }или снят :
public Constructor(int s) { elements = new Object[s]; }
Comments