Аргумент типа универсального метода Java
У меня есть проблема с явными аргументами типа универсального метода. Я знаю, что могу сделать это:
Foo.<Bar>function();
Предполагая, что существует
void <T> function() {...}
Функция в классе Foo. Точная проблема такова:
Я хотел бы загрузить некоторый контент (Android с Ion)
Эти материалы похожи (статья, Статья В блоге,...), все реализует интерфейс 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);
Все в порядке, компилируйте нормально. После бега я получаю
[5]}Java.яз..ClassCastException: com.гугл.гсон.внутренний.LinkedTreeMap не может быть приведен в COM.мой.имя_пакета.модель.ContentItem
Проблема заключается в том, что он не использует явный класс для отображения Json в pojo.
Можете ли вы предложить мне общее решение?
4 ответов:
Я думаю, что нет способа реализовать то, что вы хотите, в общем виде, используя подход TypeToken. Обратите внимание, что для работы маркеров типов необходимо создать анонимный внутренний класс. Делая это, вы фактически создаете новый файл класса, супертайп которого овеществляется как
List<Article>. Другими словами, это похоже на следующее объявление:class ArticleToken extends TypeToken<List<Article>> { ... }Если бы вы сами написали приведенное выше объявление, вы бы заметили, что classfile ArticleToken.класс следит за развитием событий. общий супертип в так называемом атрибуте
Signature(см. JVMS). Следовательно, этот трюк позволяет получить доступ к такому универсальному супертипу позже, вызвавClass.getGenericSupertype. Другими словами, это идиома для подделки овеществленных дженериков.Если вы превратите свой код в универсальный метод и замените артикль переменной типа T, то получится, что созданный вами маркер типа выглядит следующим образом:
class GenericToken extends TypeToken<List<T>> { ... }Таким образом, информация о T хранится как есть в файле classfile, и если reflectiopn запрашивает общий супертип токена типа, вы просто получаете
TypeToken<List<T>>обратно, а неTypeToken<List<Article>>, Как вы ожидали, что затем вызывает проблему, которую вы видите. То, что вам нужно, чтобы сделать эту работу, - это истинные овеществленные генераторы, где привязка T к статье на сайте вызова метода повлияла бы на поведение среды выполненияnew TypeToken<List<T>>- но, к сожалению, это не так с Java, которая использует стертые генераторы.
Для десериализации в различные классы, реализующие ContentItem, я думаю, вам нужно будет использовать пользовательский экземпляр GSON, который имеет TypeAdapter.
Много лет спустя, но я подумал, что это может быть полезно для кого-то. В итоге я нашел более простое решение. Это всего лишь простая, усеченная версия, но вы можете понять идею:
public static <T> void asList(Context context, String url, Class<T[]> clazz, final FutureCallback<List<T>> callback) { Ion.with(context) .load(url) .as(clazz) .setCallback(new FutureCallback<T[]>() { @Override public void onCompleted(Exception e, T[] result) { callback.onCompleted(e, Arrays.asList(result)); } }); }И использовать как:
asList(context, url, YourObject[].class, new FutureCallback<List<YourObject>>() {...});
Как насчет использования
private <T implements ContentItem> void download(String url) { ....Поскольку классы реализуют интерфейс ContentItem, они не расширяют интерфейс
Comments