Как стирается стирание типа

Я изучаю, как работают библиотеки, которые создают объекты-прокси, я хочу понять, как они извлекают типы из объявленных методов. Например, популярная библиотека для Android – Retrofit:

interface MyService { @GET("path") Call<MyData> getData(); } 

Я был в замешательстве – как можно получить из этого интерфейса именно класс MyData, а не raw Object? Причина из моего понимания стирания типа удалит любую информацию, помещенную внутри общих фигурных скобок.

Я написал несколько тестовых кодов, и для меня удивительно, что очень легко получить тип из такого кода:

 @org.junit.Test public void run() { Method[] methods = Test.class.getDeclaredMethods(); Method testMethod = methods[0]; System.out.println(testMethod.getReturnType()); ParameterizedType genericType = ((ParameterizedType) testMethod.getGenericReturnType()); Class<Integer> clazz = (Class<Integer>) genericType.getActualTypeArguments()[0]; System.out.println(clazz); } interface Test { List<Integer> test(); } 

Он выглядит немного грязным, но он работает и печатает Integer . Это означает, что у нас есть типы во время выполнения. Также я читал о другом грязном трюке с анонимными классами:

 System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass()); 

печатает raw AbstractList<E> то время как этот код

 System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass()); 

печатает ArrayList<Integer> .

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

 inline fun <reified T> test() { print(T::class) } 

И теперь меня полностью путают с механизмом стирания типа.

  1. Может кто-нибудь объяснить, почему иногда он хранит информацию, а иногда нет?
  2. Почему дженерики не реализованы обычным образом в Java? Да, я читал, что это может сломать совместимость с предыдущими версиями, но я хочу понять, как это сделать. Почему общий тип возвращаемого значения не нарушает ничего, кроме new ArrayList<Integer> ?
  3. Почему анонимные классы имеют общий тип и не стираются?

Обновлено: 4. Как создаются обобщенные дженерики в Котлине и почему такая классная вещь не может быть реализована на Java?

Здесь объясняется довольно четкое представление о том, как работают восстановленные дженерики. @Mibac

Вы можете использовать только подтверждение в сочетании с встроенной функцией. Такая функция заставляет компилятор копировать байт-код функции в любое место, где используется функция (функция «встроена»). Когда вы вызываете встроенную функцию с типом reified, компилятор знает фактический тип, используемый как аргумент типа, и модифицирует сгенерированный байт-код для непосредственного использования соответствующего класса. Поэтому вызовы типа myVar: T становится myVar – String, если аргумент типа был String, в байт-коде и во время выполнения.

Может кто-нибудь объяснить, почему иногда он хранит информацию, а иногда нет?

На уровне jvm есть атрибут Signature, который:

записывает подпись (§4.7.9.1) для класса, интерфейса, конструктора, метода или поля, декларация которого на языке программирования Java использует переменные типа или параметризованные типы.

Одна из причин, почему вы, возможно, захотите иметь это, заключается в том, что, например, компилятор должен знать фактический тип аргумента some_method который исходит из прекомпилированного some.class (который исходит от стороннего some.jar ). В этом сценарии, предполагая, что метод принимает Object может нарушать предположения, с которыми был скомпилирован some.class и вы не можете вызвать some_method в виде типа.

Почему анонимные классы имеют общий тип и не стираются?

Когда вы звоните:

 System.out.println(new ArrayList<Integer>().getClass().getGenericSuperclass()); 

… ничего не определяется в терминах классов jvm , а это:

 System.out.println(new ArrayList<Integer>() { }.getClass().getGenericSuperclass()); 

… фактически определяет класс на уровне jvm , анонимный albiet на уровне jagu laguage .

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

Почему дженерики не реализованы обычным образом в Java? Да, я читал, что это может сломать совместимость с предыдущими версиями, но я хочу понять, как это сделать. Почему тип возвращаемого типа не нарушает ничего, кроме нового ArrayListdoes?

Как создаются обобщенные дженерики в Котлине и почему такая классная вещь не может быть реализована на Java?

Я не думаю, что вы можете дать объективный ответ, который не основан на мнениях. Более того, я предлагаю либо расщепление, либо сужение сферы вашего вопроса.