Что такое 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?

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

5 ответов

Решение

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

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

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

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

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

Относительно усовершенствованных дженериков в Java

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

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

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

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

Образец кода

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

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

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

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

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

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

Я могу придумать простое объяснение - они сознательно предпочли простоту раздуванию кода, поскольку из-за стирания после компиляции кода существует только одна реализация универсального класса и его подтипов.

C++, с другой стороны, создает отдельную версию кода для каждой реализации.

Другие вопросы по тегам