В чем разница между «массивами параметризованного типа (List <String>[])» и «массивом переменных типа (T [])»? - PullRequest
0 голосов
/ 30 мая 2019

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


Мое понимание причины, по которой массивы параметризованного типа не допускаются:

Integer[] b = {1,2,3};
Object[] a = b;
String[] c = {"123"};
a[0] = c; // Exception in thread "main" java.lang.ArrayStoreException
Integer i = b[0];
// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;

// Run-time error: ClassCastException.
String s = lsa[1].get(0);

Эти две частикода имеет такую ​​же структуру.

В части 1 Object[] a = b заставляет a и b указывать на одну и ту же переменную b.Так что a[0] = c на самом деле меняется b.Этот тип операции не вызовет никакого предупреждения при компиляции, но вызовет исключение ArrayStoreException.

Во второй части Object o = lsa; Object[] oa = (Object[]) o; заставляет oa и lsa указывать на одну и ту же переменную lsa, иoa[1] = li; на самом деле меняется lsa.

Поэтому я думаю, что именно по этой причине нельзя разрешать использование массивов параметризованного типа.

Если я правильно понимаю, случай, подобный T[] myArray, также следует отклонить.Поскольку приведенный ниже код также имеет такую ​​же структуру.

import java.util.*;

public class Test {
    public static <T> void test(T[] t) {
        Object[] a = t;
        String[] c = {"123"};
        a[0] = c;
        T i = t[0];
    }

    public static void main(String[] args) {
        Integer[] t = {1,2,3};
        test(t);
    }
}

Но в руководстве по Java

<T> T[] makeArray(T t) {
    return new T[100]; // Error.
}

Причина, которую он дает,

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

Не та причина, которую я ожидаю.

Там также написано

Чтобы обойти эти ограничения, можно использовать литералы классов в качестве токенов типа времени выполнения

Так что я нашел пример в Generics gotchas .Код в разделе «Дорога не занята»:

public class ArrayList<V> implements List<V> {
  private V[] backingArray;
  private Class<V> elementType;

  public ArrayList(Class<V> elementType) {
    this.elementType = elementType;
    backingArray = (V[]) Array.newInstance(elementType, DEFAULT_LENGTH);
  }
}

Кажется, использование литералов класса уже решает проблему использования массива переменной типа (T []).Но проблема структуры, о которой я говорил в начале, все еще существует.Вспомните, я думаю, что структура всех трех частей кода не приводит к предупреждению компиляции, а к ошибке времени выполнения.

Итак, что не так с моим пониманием?

Большое вам спасибо за ваше время :)

Ответы [ 2 ]

0 голосов
/ 30 мая 2019

Как вы обнаружили, вы не можете присвоить элемент массиву типа reifiable:

String[] ss = {""};
Object[] oo = ss;
oo[0] = 0; // ArrayStoreException

Это потому, что тип элемента массива проверяется во время выполнения, и это может быть потому, что вся информация о типе все еще доступна.

Но с универсальным массивом, скажем List<String>[], бит "String" недоступен во время выполнения. Итак, это будет (условно) работать:

List<String>[] lss = {Arrays.asList()};
Object[] oo = lss;
List<Integer> is = Arrays.asList(0);
oo[0] = is;

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

Но тогда это бы не сработало, если бы вы сделали:

String s = lss[0].get(0); // ClassCastException

И этот сбой может произойти далеко от задания, которое его вызвало.

Таким образом, создание общего массива запрещено.


С точки зрения разницы между List<String>[] и T[]: на самом деле нет никакой разницы. List<String> - допустимый тип для T.

Дело в том, что вы просто не можете создать экземпляр первого: единственное значение, которое может безопасно принимать ссылка этого типа, это null.

Кажется, использование литералов класса уже решает проблему "переменные типа не существуют во время выполнения"

Нет, поскольку литералы класса существуют только для непараметризованного типа: вы можете создать ArrayList<String> таким образом, передав String.class в качестве типа элемента; вы не можете создать ArrayList<ArrayList<String>>, потому что нет ArrayList<String>.class.

0 голосов
/ 30 мая 2019

Для вас первый вопрос

почему массивы параметризованного типа не разрешены?

Я нахожу ответ

Можно ли создать массив, тип компонента которого является конкретным параметризованным типом?

Нет, потому что он не является типобезопасным.


Затем я пытаюсь объяснить, почему пример кода вызывает исключение?

  1. Java-компиляция не обнаружила проблему во время компиляции, каждый приведенный тип выглядит как допустимый.(ArrayStoreException - RuntimeException)

  2. , когда вы помещаете элемент String в Integer Array, возникает исключение.Вы можете использовать javac и javap, чтобы просмотреть байт-код.Тип T[] t - Object [], поэтому переменная a (ссылочный тип) будет ссылаться на Integer[] Object.исключение произошло, когда вы поместили элемент String в Integer Array

Я думаю, что проблема не связана с универсальным java, поэтому я изменяю следующий пример, чтобы воспроизвести исключение.

public class Test {

    public static void main(String[] args) {
        Integer[] t = {1,2,3};
        Object[] a = t;
        a[2] = "aaa";
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...