Какова причина, почему Java не позволяет нам делать
private T[] elements = new T[initialCapacity];
Я мог понять, что .NET не позволил нам сделать это, так как в .NET у вас есть типы значений, которые во время выполнения могут иметь разные размеры, но в Java все виды T будут ссылками на объекты, поэтому имеют одинаковый размер ( поправьте меня если я ошибаюсь).
Какова причина?
java
generics
type-erasure
пожрал Элизиум
источник
источник
Ответы:
Это потому, что массивы Java (в отличие от обобщенных) содержат во время выполнения информацию о типе его компонента. Таким образом, вы должны знать тип компонента при создании массива. Поскольку вы не знаете, что
T
происходит во время выполнения, вы не можете создать массив.источник
ArrayList <SomeType>
сделать тогда?new ArrayList<SomeType>()
? Универсальные типы не содержат параметр типа во время выполнения. Параметр типа не используется при создании. Нет разницы в коде, созданномnew ArrayList<SomeType>()
илиnew ArrayList<String>()
илиnew ArrayList()
вообще.ArrayList<T>
работает с ней »private T[] myArray
. Где-то в коде он должен иметь массив универсального типа T, так как?T[]
. У него есть массив типа времени выполненияObject[]
, и либо 1) исходный код содержит переменнуюObject[]
(это так в последней версии Oracle Java source); или 2) исходный код содержит переменную типаT[]
, которая является ложью, но не вызывает проблем из-заT
удаления внутри области видимости класса.Quote:
(Я считаю, что это Нил Гафтер , но я не уверен)
Посмотрите это в контексте здесь: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316
источник
new T()
. Каждый массив в Java по своему дизайну хранит тип компонента (т.е.T.class
) внутри него; поэтому вам нужен класс T во время выполнения для создания такого массива.new Box<?>[n]
, что иногда может быть достаточно, хотя это не поможет в вашем примере.Box<String>[] bsa = new Box<String>[3];
что-то изменилось в java-8 и выше, я полагаю?Не предоставив достойного решения, вы просто получите что-то худшее ИМХО.
Общая работа заключается в следующем.
заменяется на (при условии, что T расширяет Object, а не другой класс)
Я предпочитаю первый пример, однако больше академических типов предпочитают второй, или просто предпочитают не думать об этом.
Большинство примеров того, почему вы не можете просто использовать Object [], в равной степени применимы к List или Collection (которые поддерживаются), поэтому я вижу их как очень плохие аргументы.
Примечание: это одна из причин, по которой сама библиотека коллекций не компилируется без предупреждений. Если этот вариант использования не может быть поддержан без предупреждений, что-то в корне сломано с моделью универсальных моделей IMHO.
источник
String[]
(или если вы сохраните его в поле, которое общедоступного типаT[]
, и кто-то получит его), он получит исключение ClassCastException.T[] ts = (T[]) new Object[n];
это плохая идея: stackoverflow.com/questions/21577493/…T[] ts = new T[n];
был верным примером. Я сохраню голосование, потому что его ответ может вызвать проблемы и замешательство у других разработчиков, а также не по теме. Также я перестану комментировать по этому поводу.Массивы ковариантны
Эта последняя строка будет хорошо скомпилирована, но если мы запустим этот код, мы получим
ArrayStoreException
потому что мы пытаемся поместить double в целочисленный массив. Тот факт, что мы обращаемся к массиву через ссылку Number, здесь не имеет значения, важно то, что массив является массивом целых чисел.Это означает, что мы можем обмануть компилятор, но мы не можем обмануть систему типов во время выполнения. И это так, потому что массивы - это то, что мы называем типом reifiable. Это означает, что во время выполнения Java знает, что этот массив был фактически создан как массив целых чисел, к которым просто случается обращение через ссылку типа
Number[]
.Итак, как мы видим, одна вещь - это фактический тип объекта, другая вещь - это тип ссылки, которую мы используем для доступа к ней, верно?
Проблема с Java Generics
Теперь проблема с универсальными типами в Java заключается в том, что информация о типе для параметров типа отбрасывается компилятором после завершения компиляции кода; поэтому информация этого типа недоступна во время выполнения. Этот процесс называется стиранием типа . Существуют веские причины для реализации таких обобщений в Java, но это длинная история, и она связана с бинарной совместимостью с уже существующим кодом.
Рассмотрим теперь следующий небезопасный код:
Если компилятор Java не мешает нам делать это, система типов во время выполнения также не может остановить нас, потому что во время выполнения нет никакого способа определить, что этот список должен быть только списком целых чисел. Среда выполнения Java позволила бы нам помещать все, что мы хотим в этот список, когда он должен содержать только целые числа, потому что, когда он был создан, он был объявлен как список целых чисел. Вот почему компилятор отклоняет строку № 4, потому что это небезопасно и, если разрешено, может нарушить предположения системы типов.
Поэтому разработчики Java позаботились о том, чтобы мы не могли обмануть компилятор. Если мы не можем обмануть компилятор (как мы можем сделать с массивами), то мы не можем обмануть и систему типов во время выполнения.
Таким образом, мы говорим, что универсальные типы не подлежат повторному определению, поскольку во время выполнения мы не можем определить истинную природу универсального типа.
Я пропустил некоторые части этих ответов, вы можете прочитать полную статью здесь: https://dzone.com/articles/covariance-and-contravariance
источник
Причина, по которой это невозможно, состоит в том, что Java реализует свои Generics исключительно на уровне компилятора, и для каждого класса генерируется только один файл класса. Это называется стиранием типа .
Во время выполнения скомпилированный класс должен обрабатывать все свои использования с одним и тем же байт-кодом. Таким образом,
new T[capacity]
абсолютно не знаю, какой тип должен быть создан.источник
Ответ уже был дан, но если у вас уже есть экземпляр T, вы можете сделать это:
Надеюсь, я мог помочь, Ferdi265
источник
T[] ts = t.clone(); for (int i=0; i<ts.length; i++) ts[i] = null;
.T[] t
, то будет(T[]) Array.newInstance(t.getClass().getComponentType(), length);
. я потратил несколько раз, чтобы выяснитьgetComponentType()
. Надеюсь, что это помогает другим.t.clone()
не вернетсяT[]
. Потому чтоt
это не Array в этом ответе.Основная причина заключается в том, что массивы в Java ковариантны.
Там есть обзор хорошо здесь .
источник
String[]
к немуObject
и сохранитьInteger
в нем. Затем им пришлось добавить проверку типа во время выполнения для массива store (ArrayStoreException
), потому что проблема не могла быть обнаружена во время компиляции. (В противном случае наInteger
самом деле может застрять в aString[]
, и вы получите сообщение об ошибке, когда попытаетесь получить его, что будет ужасно.) ...Object
должен был бытьObject[]
в моем первом комментарии.Мне нравится ответ, косвенно предоставленный Gafter . Тем не менее, я предлагаю это неправильно. Я немного изменил код Gafter. Он компилируется и работает какое-то время, а затем бомбит, где Гафтер предсказал
Выход
Таким образом, мне кажется, вы можете создавать универсальные типы массивов в Java. Я неправильно понял вопрос?
источник
Я знаю, что немного опоздал на вечеринку, но решил, что смогу помочь любым будущим гуглерам, так как ни один из этих ответов не решил мою проблему. Ответ Ferdi265 очень помог, хотя.
Я пытаюсь создать свой собственный связанный список, поэтому следующий код работает для меня:
В методе toArray () заключается способ создания массива универсального типа для меня:
источник
В моем случае я просто хотел массив стеков, что-то вроде этого:
Поскольку это было невозможно, я использовал следующее в качестве обходного пути:
Ужасно, но Ява счастлива.
Примечание: как упомянуто BrainSlugs83 в комментарии к вопросу, вполне возможно иметь массивы обобщений в .NET
источник
Из учебника Oracle :
Для меня это звучит очень слабо. Я думаю, что любой, кто обладает достаточным пониманием дженериков, будет в порядке и даже ожидает, что ArrayStoredException не будет выброшено в таком случае.
источник
Безусловно, должен быть хороший способ обойти это (возможно, с помощью рефлексии), потому что мне кажется, что это именно то, что
ArrayList.toArray(T[] a)
делает. Я цитирую:Таким образом, одним из способов было бы использовать эту функцию, то есть создать
ArrayList
объект, который вы хотите в массиве, а затем использоватьtoArray(T[] a)
для создания фактического массива. Это не было бы быстро, но вы не упомянули свои требования.Так кто-нибудь знает, как
toArray(T[] a)
это реализовано?источник
Array.newInstance()
. Вы найдете упомянутое во многих вопросах, которые задают, как создать массив с типом, неизвестным во время компиляции. Но ОП специально спрашивал, почему вы не можете использоватьnew T[]
синтаксис, а это другой вопросЭто потому, что дженерики были добавлены в java после того, как они его сделали, поэтому это немного неуклюже, потому что первоначальные создатели java думали, что при создании массива тип будет указан при его создании. Так что это не работает с генериками, поэтому вы должны сделать E [] array = (E []) new Object [15]; Это компилируется, но выдает предупреждение.
источник
Если мы не можем создать экземпляры универсальных массивов, почему язык имеет универсальные типы массивов? Какой смысл иметь тип без объектов?
Единственная причина, по которой я могу думать, это varargs -
foo(T...)
. В противном случае они могли бы полностью очистить универсальные типы массивов. (Ну, им действительно не нужно было использовать массив для varargs, поскольку varargs не существовало до 1.5 . Это, вероятно, еще одна ошибка.)Так что это ложь, вы можете создавать экземпляры универсальных массивов через varargs!
Конечно, проблемы с универсальными массивами все еще актуальны, например,
Мы можем использовать этот пример, чтобы фактически продемонстрировать опасность универсального массива.
С другой стороны, мы использовали общие varargs в течение десятилетия, и небо еще не падает. Таким образом, мы можем утверждать, что проблемы преувеличиваются; это не так уж много значит. Если явное создание универсального массива разрешено, у нас будут ошибки здесь и там; но мы привыкли к проблемам стирания, и мы можем жить с этим.
И мы можем указать, чтобы
foo2
опровергнуть утверждение о том, что спецификация удерживает нас от проблем, от которых, по их утверждениям, они не хотят. Если бы у Sun было больше времени и ресурсов для 1,5 , я думаю, они могли бы достичь более удовлетворительного решения.источник
Как уже упоминалось, вы, конечно, можете создавать с помощью некоторых хитростей .
Но это не рекомендуется.
Поскольку стирание типа и, что более важно,
covariance
массив in, который просто разрешает массив подтипов, может быть назначен массиву супертипов, что вынуждает вас использовать явное приведение типов при попытке вернуть значение, вызывая время выполнения,ClassCastException
которое является одной из основных целей. которые дженерики пытаются устранить: более строгие проверки типов во время компиляции .Более прямой пример можно найти в Effective Java: пункт 25 .
ковариация : массив типа S [] является подтипом T [], если S является подтипом T
источник
Если класс использует в качестве параметризованного типа, он может объявить массив типа T [], но не может напрямую создать экземпляр такого массива. Вместо этого общий подход заключается в создании экземпляра массива типа Object [], а затем приведении сужения к типу T [], как показано ниже:
источник
Попробуй это:
источник