2016-06-27 2 views
13

Java представил стирание стилей с generics в Java 5, чтобы они работали на старых версиях Java. Это была комбинация совместимости. С тех пор мы потеряли эту совместимость [1][2] [3] - bytecode может быть запущен в более поздних версиях JVM, но не ранее. Это выглядит как худший вариант: мы потеряли информацию о типе, и мы по-прежнему не можем запустить байт-код, скомпилированный для более ранних версий JVM в старых версиях. Что случилось?Почему бы не удалить стирание стилей из следующего JVM?

В частности, я спрашиваю, есть ли какие-либо технические причины, по которым стирание стилей не может быть удалено в следующей версии JVM (предполагая, что, как и предыдущие выпуски, его байт-код не сможет работать в последней версии так или иначе).

[3]: Тип стирания может быть передан в обратном порядке способом, аналогичным retrolambda для тех, кому это действительно нравится.

Редакция: Я думаю, что обсуждение определения обратной и обратной совместимости заслоняет вопрос.

+6

Обратная совместимость никогда не терялась. Старая программа Java 1.1, скорее всего, по-прежнему будет работать гладко на JRE 8 VM, о чем говорят связанные сообщения. – Tunaki

+0

@ Тунаки исправьте меня, если я ошибаюсь, но это передовая совместимость: ваш код будет работать на будущих JVM. Если мы удалим стирание типа в JVM версии X, оно все равно будет удалено в JVM X + 1, поэтому наш код все равно будет работать. – Prime

+1

Не совсем, см. Также http://stackoverflow.com/questions/4692626/is-jdk-upward-or-backward-compatible – Tunaki

ответ

9

В какой-то степени стирание будет удалено в будущем с помощью project valhalla, чтобы включить специализированные реализации для типов значений.

Или, если это более точно, тип стирания на самом деле означает отсутствие специализации типа для дженериков, а valhalla будет вводить специализацию по примитивам.

Конкретно я спрашиваю, есть ли какие-либо технические причины, тип стирание не может быть удалены в следующей версии Performance JVM

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

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

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

6

Ваше понимание обратной совместимости неверно.

Желаемая цель для нового JVM, чтобы иметь возможность запускать старый код библиотеки правильно и неизменными даже при нового кода. Это позволяет пользователям надежно обновлять свои версии Java даже в гораздо более новых версиях, чем для этого кода.

+0

Как устранить стирание стилей? Старый код не будет полагаться на информацию о типе (потому что он не существует), и новый код будет запускаться только на новом JVM. Все могут легко обновляться. – Prime

+1

@Prime - Это не так. Однако вы используете свою фиктивную идею обратной совместимости в качестве оправдания для выбрасывания >> реальной << обратной совместимости. Кроме того, ваше смелое утверждение о том, что «каждый может легко модернизировать», требует гораздо большего оправдания. Я никому не верю. –

+0

Стирание стира STEPENC является субтрактивным - если ваша программа предполагает, что информация о типе была стерта, она будет работать нормально, пока в пути к классам есть java.util.List [Object]. Добавление дополнительной информации о типе может означать нечто вроде добавления специализаций, поэтому JVM будет знать, например, java.util.Список [Целое] и может избежать проверки типа времени выполнения. Эту информацию можно получить во время выполнения, используя типы reified (как это делает scala, см. Http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html). Точно так же, как вы всегда можете получить информацию, доступную при компиляции ... – Prime

12

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

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

Если вы пишете new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>() вы не только создаете три объекта, вы потенциально создает три дополнительных мета-объектов, отражающих типы, ArrayList<String>, ArrayList<Number> и ArrayList<Object>, если они не существовали до этого.

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

Это, разумеется, умножается на тысячи разных типов списков типов, подразумевающих тысячи разных типов итераторов, тысяч разделителей и потоковых воплощений, даже не считая внутренних классов реализации.

И это даже влияет на места без выделения объекта, которые в настоящее время исследуют стирание типа под капотом, например. Collections.emptyList(), Function.identity() или Comparator.naturalOrder() и т. Д. Возвращают один и тот же экземпляр при каждом вызове. Если вы настаиваете на том, чтобы учитываемый particalar тип общего типа отражался, он больше не работает. Так что, если вы пишете

List<String> list=Collections.emptyList(); 
List<Number> list=Collections.emptyList(); 

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


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

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


Ответ до сих пор только рассмотрел легкий аспект удаления типа стирания, то желание вникать тип фактического экземпляра. Фактический экземпляр имеет конкретный тип, о котором можно сообщить. Как упоминалось в this comment от user the8472, требование удаления стирания типа часто также подразумевает желание быть отличным до (T) или создать массив через new T[] или получить доступ к типу переменной типа через T.class.

Это поднимет истинный кошмар. Переменная типа - это другой зверь, чем фактический тип конкретного экземпляра. Переменная типа может быть разрешена, например, ? extends Comparator<? super Number>, чтобы назвать один (довольно простой) пример. Предоставление необходимой метаинформации подразумевает, что не только распределение объектов становится намного дороже, каждый вызов метода может налагать эти дополнительные затраты еще шире, так как теперь мы не только говорим о комбинации родовых классов с фактическими классами, но и также всевозможные подстановочные комбинации, даже вложенные родовые типы.

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

Помимо большой проблемы с производительностью, сложность вызывает еще одну проблему. Если вы посмотрите список отслеживания ошибок javac или связанные с ним вопросы Stackoverflow, вы можете заметить, что этот процесс не только сложный, но и подверженный ошибкам. В настоящее время каждая младшая версия javac содержит изменения и исправления в отношении соответствия подписи общего типа, влияющие на то, что будет принято или отклонено. Я уверен, вам не нужны внутренние операции JVM, такие как приведение типов, назначения переменных или массивы массивов, чтобы стать жертвой этой сложности, имея другое представление о том, что является законным или нет в каждой версии, или внезапно отвергает то, что javac приняло на время компиляции из-за правил несоответствия.

+2

вы предполагаете, что он будет использоваться только через отражение. но не стертые дженерики означают, что переменные типа в классах (например, T) были бы реальными, т. е. листинг через '(T)' был бы фактическим отбрасыванием и обеспечивал бы быстрое поведение, 'new T []' создавал бы array 'T.class' может дать вам объект класса. Кроме того, хотя вы правы в том, что объекты метаданных должны быть созданы, они все равно будут только постоянным фактором по сравнению с количеством классов, так как общие подписи в конечном итоге обусловлены вызовами распределения, из которых только конечная сумма для каждого класса. – the8472

+2

@ the8472: Я сосредоточился на части * easy *, поскольку то, что вы описываете, значительно ухудшает ситуацию. Поскольку существуют также общие * методы *, описанные вами функции будут подразумевать, что каждый * вызов * может нести эти накладные расходы, а не только сайты размещения объектов. Я потратил свой ответ, чтобы обсудить некоторые из связанных с этим вопросов. Конечно, количество сайтов вызовов также конечно, но число звезд в нашей вселенной также может быть конечным ... – Holger

+1

Хорошая точка, различая границы типов и конкретные типы, добавляет еще один источник сложности. – the8472

1

Конкретно я спрашиваю, есть ли какие-либо технические причины, тип стирания не могут быть удалены в следующей версии виртуальной машины Java (предполагающие, как и в предыдущих выпусках, его байт-код не сможет работать на последняя версия в любом случае).

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

Да, это техническая причина, поскольку обратная совместимость является одним из важнейших свойств языка Java/платформы. По крайней мере, это перспектива для клиентов Oracle и Oracle.

Если стирание типа перестает работать, внезапно сотни миллионов долларов инвестиций в программное обеспечение на основе Java, производимое многими, многими предприятиями за последние 20 лет, находятся под угрозой.


Задайте себе это. Почему Sun/Oracle не удалил Thread.stop()?

Смежные вопросы