Аргумент типа обобщенного метода Java

У меня проблема с аргументами явного типа generic метода. Я знаю, что могу сделать это:

Foo.<Bar>function(); 

Предполагая, что существует

 void <T> function() {...} 

Функция в классе Foo. Точная проблема:

  • Я хотел бы загрузить некоторый контент (Android с Ion )

  • Эти материалы похожи (Article, BlogArticle, …), все реализуют интерфейс ContentItem

  • На данный момент загрузка выглядит так:

Новости, например

 private void downloadNews() { Ion.with(this) .load(URL_NEWS) .as(new TypeToken<List<Article>>(){}) .setCallback(new FutureCallback<List<Article>>() { @Override public void onCompleted(Exception e, List<Article> result) { // do something with result } }); } 

Если я хочу загрузить статьи в блоге, мне нужно изменить только URL-адрес и класс статьи (для BlogArticle).

Я попытался сделать такую ​​общую функцию:

 private <T extends ContentItem> void download(String url) { Ion.with(this) .load(url) .as(new TypeToken<List<T>>(){}) .setCallback(new FutureCallback<List<T>>() { @Override public void onCompleted(Exception e, List<T> result) { // do something with result } }); } 

И вызовите эту функцию

 this.<Article>download(url); 

Все в порядке, хорошо компилируйся. После запуска я получаю

Java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap не может быть добавлено в com.my.packagename.model.ContentItem

Проблема в том, что он не использует явный класс для отображения Json в pojo.

Можете ли вы предложить мне общее решение?

Чтобы десериализовать различные классы, которые реализуют ContentItem, я думаю, вам нужно будет использовать собственный экземпляр GSON, который имеет TypeAdapter.

http://www.javacreed.com/gson-typeadapter-example/

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

 class ArticleToken extends TypeToken<List<Article>> { ... } 

Если вы сами напишете вышеуказанное заявление, вы заметите, что classfile ArticleToken.class отслеживает общий супертип в так называемом атрибуте Signature (см. JVMS ). Следовательно, этот трюк позволяет вам позже получить доступ к этому универсальному супертипу, вызвав Class.getGenericSupertype . Другими словами, это идиома для подделки редифицированных дженериков.

Если вы превратите свой код в общий метод и замените статью переменной типа T, произойдет то, что созданный вами маркер типа выглядит так:

 class GenericToken extends TypeToken<List<T>> { ... } 

Таким образом, информация о T сохраняется как в файле классов, и если reflectiopn запрашивает общий супертип маркера типа, вы просто получаете TypeToken> back, а не TypeToken>, как вы ожидали бы, что затем вызывает проблему, которую вы видите , То, что вам нужно для выполнения этой работы, – это истинные обобщенные обобщения, где привязка T к статье на узле вызова метода повлияет на поведение во время выполнения new TypeToken<List<T>> но, к сожалению, это не относится к Java, которая использует стираемые дженерики.

Как насчет использования

 private <T implements ContentItem> void download(String url) { .... 

Поскольку классы реализуют интерфейс ContentItem, они не расширяют интерфейс