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

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

31 голосов
/ 18 октября 2011

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

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

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

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

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

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

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

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

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

Ответы [ 5 ]

22 голосов
/ 18 октября 2011

Вы не можете иметь массив, потому что массив требует необработанного типа. Вы вводите его во второй экземпляр, что делает его соответствующим определенному типу, и, следовательно, является законным (однако это невозможно сделать выводом). Список допустим, поскольку 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

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

Затем в учебнике говорится следующее:

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

5 голосов
/ 18 октября 2011

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

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

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

На самом деле 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 голосов
/ 18 октября 2011

У меня был похожий вопрос сам - 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

. Так как я могу перебросить List [] в Object [], затем засунуть что-то неверное в Object [], а затем обратиться к неверно изСсылка на список, через приведенную ссылку, это плохо / запрещено?Но только с новым?

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

2 голосов
/ 18 октября 2011

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

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

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

Или с Java SE 7:

 List<List<Key>> b = new ArrayList<>(10);
1 голос
/ 18 октября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...