Что такое Reified Generics? Как они решают проблемы с стиранием шрифта и почему их нельзя добавить без серьезных изменений?

Я прочитал на эту тему блог Нила Гафтера и мне все еще неясно по ряду пунктов.

Почему невозможно создать реализации API коллекций, которые сохраняют информацию о типе с учетом текущего состояния Java, JVM и существующих API коллекций? Не могли бы они заменить существующие реализации в будущей версии Java таким образом, чтобы была сохранена обратная совместимость?

Например:

List<T> list = REIList<T>(T.Class);

Где REIList выглядит примерно так:

public REIList<T>() implements List {
  private Object o;
  private Class klass;

  public REIList(Object o) {
    this.o = o;
    klass = o.getClass();
  }
... the rest of the list implementation ...

И методы используют Object o и Class klass для получения информации о типе.

Почему для сохранения общей информации о классе требуются языковые изменения, а не просто изменение реализации JVM?

Что я не понимаю?


person sal    schedule 18.05.2009    source источник


Ответы (4)


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

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

person ghempton    schedule 18.05.2009
comment
Его исходный и объектный код, написанный для старых JVM, может работать на новых JVM без каких-либо изменений (в исходном коде будут проблемы с перечислением в качестве идентификатора, но это не универсальные шаблоны как таковые). - person Tom Hawtin - tackline; 19.05.2009
comment
JVM с более низкими версиями все еще могут понимать общие классы ‹- я думаю, вы получите UnsupportedClassVersionError, когда попытаетесь запустить программу, скомпилированную с более новой версией JDK в более старой версии JRE / JVM. Так что фактом должно быть то, что сказали Том Хотин и Ричард Гомес, а это прямо противоположное. - person blackr1234; 21.03.2018

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

По поводу стирания шрифта

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

Что касается обобщенных дженериков в Java

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

Я написал статью на эту тему:

http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html.

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

Образец кода

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

person Richard Gomes    schedule 27.01.2011
comment
Это не имеет ничего общего с овеществленными дженериками. Да, универсальные шаблоны в объявлениях классов, такие как типы в общих параметрах, типы полей, типы в сигнатурах методов, тип суперкласса, тип включающего класса внутренних классов (в анонимных классах нет ничего особенного) являются хранится в байт-коде, потому что другие классы, которые используют этот класс и имеют только файл .class, должны знать эти вещи, чтобы обеспечить соблюдение универсальных шаблонов. Однако обсуждение обобщенных и нерефицированных универсальных шаблонов говорит об универсальном типе исполняемых объектов, который не имеет отношения к тому, о чем вы говорите выше. - person newacct; 15.06.2012
comment
@newacct: Да, это так. Да, внутренние классы особенные с точки зрения сохранения информации времени компиляции, которую можно получить во время выполнения. По определению, когда вы можете во время выполнения получать информацию о типах времени компиляции из общих классов ... что у вас есть? - person Richard Gomes; 30.06.2012
comment
Вы также можете получить информацию времени компиляции о суперклассе, суперинтерфейсах, полях, методах и т. Д. Для ЛЮБОГО класса. - person newacct; 30.06.2012
comment
@newacct: Верно, но анонимные классы предоставляют дополнительную функцию: вы можете получать информацию о типах для их универсальных типов во время выполнения. Другими словами: у вас действительно есть обобщенные дженерики для анонимных классов. См. Этот тестовый пример: jquantlib.org / сайты / jquantlib / xref-test / org / jquantlib / testsuite / - person Richard Gomes; 05.07.2012
comment
Нет, вы просто извлекаете параметр универсального типа, с помощью которого он расширил свой суперкласс. Выражение создания анонимного класса создает экземпляр класса, который является подклассом указанного вами типа. final K k1 = new K<java.lang.Double>() {}; в точности совпадает с локальным классом class Foo extends K<java.lang.Double> {}; final K k1 = new Foo(); - person newacct; 05.07.2012
comment
@newacct :: эта ссылка не работает. Проконсультируйтесь с этим: bazaar.launchpad.net/~frgomes/jquantlib/trunk/view/1362/ - person Richard Gomes; 10.03.2014
comment
@newacct :: Дело в том, что решение, которое я представил, приближает вас к овеществленным дженерикам. Сгенерированный код (вызываемый сайт) по-прежнему является только одной копией для всех используемых активных типов (на вызывающем сайте). Если вы скажете мне, что по этой причине он не овеществлен на самом деле ... да ... я согласен. Но в контексте статьи Нила Гафтера вопрос заключается в извлечении информации о времени выполнения, как если бы вы обновили дженерики. Если разработчик ценит истинные обобщенные дженерики с множеством копий на сайте вызываемого ... ну да ладно ... лучше поищите другой язык программирования :) - person Richard Gomes; 10.03.2014
comment
Вопрос вовсе не в этом. Речь идет о том, почему реифицируемая реализация, описанная OP, требует изменений языка, а не просто изменений JVM. Ваш ответ ни о чем другом. - person user207421; 09.07.2017
comment
В статье ни разу не использовался термин овеществление, и как он отвечает на вопрос? Кстати, хорошая статья, в своей собственной концепции - person Masoud Dadashi; 12.07.2017

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

С другой стороны, дженерики .NET фактически создают новые типы - List<String> в C # - это другой тип, чем List<Int>. В Java, под крышками, это одно и то же - List<Object>. Это означает, что если у вас есть по одному из каждого, вы не можете посмотреть на них во время выполнения и увидеть, как они были объявлены - только то, что они есть сейчас.

Воссоздание универсальных шаблонов изменит это, предоставив разработчикам Java те же возможности, которые существуют сейчас в .NET.

person Harper Shelby    schedule 18.05.2009
comment
Я заключил ваш список в кавычки, чтобы типы отображались. - person David Z; 19.05.2009
comment
@ Дэвид - спасибо. Сколько раз я делал это для кого-то другого, ты думаешь, я бы вспомнил ... - person Harper Shelby; 19.05.2009
comment
Вы можете имитировать своего рода овеществленные дженерики с анонимными классами, то есть в этом случае информация о типе доступна во время выполнения. Несмотря на то, что это возможно, его применимость очень ограничена, а техника очень сложна. Reified Generics - большая ошибка; это, конечно, правда. - person Richard Gomes; 31.01.2011

Я не эксперт в этом вопросе, но, насколько я понимаю, информация о типе теряется во время компиляции. В отличие от C ++, Java не использует систему шаблонов, безопасность типов полностью обеспечивается компилятором. Во время выполнения List на самом деле всегда является списком.

Итак, я считаю, что требуется изменение спецификации языка из-за того, что информация о типе недоступна для JVM , потому что ее там нет.

person n3rd    schedule 18.05.2009
comment
Это верно в большинстве случаев, за исключением случаев, когда у вас есть анонимные классы. Я имею в виду: вы можете избежать стирания типа, используя анонимные классы. - person Richard Gomes; 31.01.2011
comment
Чтобы быть более конкретным по предмету: Java не будет генерировать несколько копий вашего кода, как C ++ для шаблонов. Java создает единственную копию на вызываемом сайте, который использует объекты. Итак, несмотря на то, что информация о типе среды выполнения доступна в случае анонимных классов, это все, что у вас есть: сама информация. Байт-код не нацелен на фактические типы, которые вы будете передавать во время выполнения. - person Richard Gomes; 10.03.2014