Generic Java Method Type Argument

I have a problem with arguments of an explicit type of a generic method. I know I can do this:

Foo.<Bar>function(); 

assuming that there is

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

in class foo. Exact problem:

  • I would like to download some content (Android with Ion )

  • This content is similar (Article, BlogArticle, ...), everything implements the ContentItem interface

  • Currently, the download looks like this:

news for example

 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 } }); } 

If I want to upload blog articles, I only need to change the URL and article class (for BlogArticle).

I tried to make such a general function:

 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 } }); } 

and call this function

 this.<Article>download(url); 

It is normal to compile. After starting, I get

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be added to com.my.packagename.model.ContentItem

The problem is that it does not use an explicit class to map Json to pojo.

Can you offer me a general solution?

+8
java android generics ion
source share
4 answers

Years later, but I thought it might be useful to someone. I ended up with a simpler solution. This is a simple, truncated version, but you can get this idea:

 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)); } }); } 

And use like:

 asList(context, url, YourObject[].class, new FutureCallback<List<YourObject>>() {...}); 
0
source share

I think there is no way to implement what you want in a universal style using the TypeToken approach. In fact, note that to use type tokens, you must create an anonymous inner class. By doing this, you actually create a new class file whose supertype is confirmed as List<Article> . In other words, as if you had the following declaration:

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

If you write the above expression yourself, you will notice that the classfile ArticleToken.class tracks the common supertype in the so-called Signature attribute (see JVMS ). Therefore, this trick allows you to access this universal supertype later by calling Class.getGenericSupertype . In other words, this is an idiom for faking modified generics.

If you turn your code into a general method and replace the article with a variable of type T, what happens is that the type marker you created looks like this:

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

So, the information about T is stored as it is in the class file, and if reflectiopn asks for a general supertype of type marker, you just get TypeToken<List<T>> back, not TypeToken<List<Article>> , as you expected, which then calls the problem you see. What you need to do this work is true generalized generalizations, where binding T to an article on the method invocation site will affect the behavior of the new TypeToken<List<T>> , but unfortunately this is not the case as in Java case that uses erasable generics.

+1
source share

To deserialize the various classes implementing the ContentItem, I think you will need to use your own instance of GSON with a TypeAdapter adapter.

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

0
source share

How about using

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

Because classes implement the ContentItem interface, they do not extend the interface.

-one
source share

All Articles