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

Я читал блог Neal Gafter и до сих пор неясно, в каком количестве баллов.

Почему невозможно создать реализации 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 class для получения информации о типе.

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

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

Ответ 1

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

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

Ответ 2

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

Что касается стирания стилей

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

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

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

Я написал статью по этому вопросу:

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

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

Пример кода

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

Ответ 3

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

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

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

Ответ 4

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

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