Аргумент типа универсального метода 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.



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

468   4  

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.

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

Много лет спустя, но я подумал, что это может быть полезно для кого-то. В итоге я нашел более простое решение. Это всего лишь простая, усеченная версия, но вы можете понять идею:

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

    Ничего не найдено.