Почему анонимный класс требуется в шаблоне токена супертипа в java

В шаблоне Нила Гафтера «маркер супертипа» (http://gafter.blogspot.com/2006/12/super-type-tokens.html), для передачи параметризованного типа использовался анонимный объект:

class ReferenceType<T>{}

/* anonymous subclass of "ReferenceType" */
ReferenceType<List<Integer>> referenceType = new ReferenceType<List<Integer>>(){

};
Type superClass = b.getClass().getGenericSuperclass();
System.out.println("super type : " + superClass);
Type genericType = ((ParameterizedType)superClass).getActualTypeArguments()[0];
System.out.println("actual parameterized type : " + genericType);

Тогда результат:

super type : com.superluli.test.ReferenceType<java.util.List<java.lang.Integer>>
actual parameterized type : java.util.List<java.lang.Integer>

Мой вопрос в том, что магия делает анонимный объект «referenceType», чтобы заставить его работать? Если я определяю явный подкласс «ReferenceType» и использую его вместо анонимного стиля, это не так, как ожидалось.

class ReferenceType<T>{}
class ReferenceTypeSub<T> extends ReferenceType<T>{}

/* explicitly(or, named) defined subclass of "ReferenceType" */
ReferenceType<List<Integer>> b = new ReferenceTypeSub<List<Integer>>();
Type superClass = b.getClass().getGenericSuperclass();
System.out.println("super type : " + superClass);
Type genericType = ((ParameterizedType)superClass).getActualTypeArguments()[0];
System.out.println("actual parameterized type : " + genericType);

Результат:

super type : com.superluli.test.ReferenceType<T>
actual parameterized type : T

person superluli    schedule 01.05.2014    source источник
comment
Неясно, какую часть (конкретно) статьи вы не понимаете, поскольку она довольно хорошо объясняет это. Класс, который он представляет, является абстрактным, потому что из-за стирания типов (что объясняется в статье) ... вот как это должно работать. В этом весь смысл статьи. Ваш второй пример - это, по сути, вся суть - это не может работать на Java из-за стирания типа.   -  person Brian Roach    schedule 01.05.2014
comment
Привет Брайан, я думаю, что это не имеет никакого отношения к стиранию шрифта, пожалуйста, поправьте меня, если я ошибаюсь. Средства стирания типов во время компиляции универсальных типов удаляются из исходных кодов, и JVM не будет использовать их для проверки типов во время выполнения. Но, с другой стороны, ни одна из этих сведений на самом деле не теряется: они по-прежнему сохраняются в файлах .class во время компиляции и, следовательно, загружаются в пулы объектов класса загрузчиком классов. И отражение Java предоставляет интерфейсы, позволяющие нам извлекать эту информацию.   -  person superluli    schedule 01.05.2014
comment
Итак, в обоих примерах типы стираются. Фактическая разница заключается в том, что в первом примере такие вещи, как ReferenceType‹List‹Integer››, были сохранены в файле .class анонимного класса, а во втором примере был сохранен ReferenceType‹T›.   -  person superluli    schedule 01.05.2014
comment
По сути, вы на один уровень слишком глубоко. Вы можете получить общий тип только из прямого суперкласса и только в том случае, если подкласс предоставляет конкретный тип в объявлении. Стирание типа приведет вас к пьянству (поверьте мне).   -  person Brian Roach    schedule 01.05.2014


Ответы (1)


Этот

ReferenceType<List<Integer>> referenceType = new ReferenceType<List<Integer>>(){

эквивалентно

public class AnonymousReferenceType extends ReferenceType<List<Integer>> {}
...
ReferenceType<List<Integer>> referenceType = new AnonymousReferenceType();

Взлом работает вокруг Class#getGenericSuperclass(), в котором говорится

Возвращает Type, представляющий прямой суперкласс сущности (класс, интерфейс, примитивный тип или пустота), представленный этим классом. Если суперкласс является параметризованным типом, объект Type возвращаемый должен точно отражать фактические параметры типа, используемые в исходном коде. Параметризованный тип, представляющий суперкласс, создается, если он не был создан ранее. См. объявление ParameterizedType для семантики процесса создания для параметризованных типов. Если этот Class представляет класс Object, интерфейс, примитивный тип или void, то возвращается null. Если этот объект представляет класс массива, то возвращается объект Class, представляющий класс Object.

Другими словами, суперкласс AnonymousReferenceType — это ParameterizedType, представляющий ReferenceType<List<Integer>>. Этот ParameterizedType имеет фактический аргумент типа, и это List<Integer>, который появляется в исходном коде.


Во втором примере, который отличается от первого,

class ReferenceType<T>{}
class ReferenceTypeSub<T> extends ReferenceType<T>{}

суперкласс (супертип) ReferenceTypeSub — это ReferenceType<T>, который является ParameterizedType, где фактический аргумент типа — это TypeVariable с именем T, что и появляется в исходном коде.


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

person Sotirios Delimanolis    schedule 01.05.2014
comment
Тогда информация о параметризованном типе List‹Integer› будет сохранена в файле AnonymousReferenceType.class. Вопрос в том, почему во 2-м примере там был сохранен литерал T, а не фактический тип List‹Integer›, который я объявил? Также, пожалуйста, обратитесь к комментариям исходного вопроса, где я разместил свое собственное мнение, но не уверен, что оно правильное. - person superluli; 01.05.2014
comment
Спустя некоторое время я это понимаю. Как и то, что вы сказали, все это исходит из объявления класса. В первом примере List‹Integer› находится в объявлении анонимного класса. а во 2-м это Т. - person superluli; 01.05.2014