Подтвердить что ты не робот

Массив общего списка

Я играю с Generic и массивами, кажется, что следующий код компилируется отлично,

ArrayList<Key> a = new ArrayList<Key>();

Но компилятор жалуется на это,

ArrayList<Key>[] a = new ArrayList<Key>[10];

Читая сообщение в stackoverflow, я понимаю, что это связано с типом Erasure, и я могу исправить его, используя

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10];

или список списка

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>();

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

4b9b3361

Ответ 1

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

Подробнее об этом читайте в главе 7.3 (стр. 15) в официальном руководстве .

Тип компонента объекта массива может не быть переменной типа или параметризованный тип, если он не является (неограниченным) подстановочным типом. Вы можете объявлять типы массивов, тип элемента которых является переменной типа или параметризованный тип, но не массивные объекты. Это, конечно, раздражает. Это ограничение необходимо, чтобы избежать таких ситуаций, как:

List<String>[] lsa = new List<String>[10]; // not really allowed
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // unsound, but passes run time store check
String s = lsa[1].get(0); // run-time error - ClassCastException

Если допустимы массивы параметризованного типа, приведенный выше пример компилировать без каких-либо непроверенных предупреждений и все же не работать во время выполнения.

Далее в этом руководстве говорится следующее:

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

Ответ 2

У меня был похожий вопрос сам - FWIW, я не нашел ответы на убедительные. Соответствующий раздел из наиболее подробного ответа (см. Ссылку на pdf) таков:

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

       List<String>[] lsa = new List<String>[10]; // not really allowed
       Object o = lsa;
       Object[] oa = (Object[]) o;
       List<Integer> li = new ArrayList<Integer>();
       li.add(new Integer(3));
       oa[1] = li; // unsound, but passes run time store check
       String s = lsa[1].get(0); // run-time error - ClassCastException

Итак, потому что я могу cat List [] для Object [], а затем вставить что-то неправильное в Object [], а затем неправильно ссылаться на ссылку List, через casted ref, это плохо/запрещено? Но только с новым?

Это еще более чем немного неясное для меня, как объявление этого с новым - это более или менее проблема, чем использование, все еще просматривая мои глаза, глядя на него в надежде, что это начнет иметь смысл или, по крайней мере, разрешите в приятное трехмерное изображение.

Ответ 3

Массив был плохим человеческим дженериком; с реальными генериками, следует избегать массивов, хотя это не всегда возможно.

Массивы ковариантны, дженерики инвариантны; в сочетании с стиранием вещи просто не подходят очень хорошо, как показано на примере в ответе Криса.

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

На самом деле Java создает типичные массивы для методов vararg, поэтому это немного лицемерно.

Вот утилиты, использующие этот факт

@SafeVarargs
static <E> E[] arrayLiteral(E... array)
{
    return array;
}

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

// usage

    List<String>[] array1 = arrayLiteral(list, list);

    List<String>[] array2 = newArray(10);

Ответ 4

Создание общих массивов не безопасно для типов (см. "Пункт 25: Предпочитает списки для массивов" "Эффективное Java - второе издание" Джошуа Блоха).

Использование:

 List<List<Key>> b = new ArrayList<List<Key>>(10);

Или с Java SE 7:

 List<List<Key>> b = new ArrayList<>(10);

Ответ 5

Массивы позволяют избежать проверки типа (как показано в ответе Криса). Таким образом, у вас может быть код, который передает все проверки компилятора (никаких "непроверенных" предупреждений от компилятора), но при запуске с ClassCastException не выполняется. Запрет на эту конструкцию создает проблему для разработчика, поэтому появляются предупреждения.