Reputation: 1386
I have an HTTP request handler that returns deserialized JSON an Object
, which is derived from an abstract Request
and then cast to the correct class. I'm having some issues making this handler return a List
though.
public class ListMyResource extends AbstractRequest
{
public boolean isCollection;
public ListTransactions()
{
this.isCollection = true;
}
public final RequestMethod REQUEST_METHOD = RequestMethod.GET;
public final String URL_METHOD = "";
public Class<ArrayList<MyResource>> getResourceClass()
{
return new ArrayList<MyResource>().getClass();
}
}
public Object getDeserializedResponse()
{
Response response = new Response(this.request, true);
try
{
if (this.request.isCollection())
{
List<this.request.getResourceClass()> list = new ArrayList<this.request.getResourceClass()>();
listType = new TypeToken<ArrayList<AbstractObject>>() { }.getType();
response.setData(deserialize.fromJson(this.getResponse(), listType));
}
else
{
response.setData(deserialize.fromJson(this.getResponse(), this.request.getClazz()));
}
}
catch (JsonSyntaxException jse)
{
MyLibrary.LOG.error("Could not parse JSON", jse);
response.setRequestWasSuccessfull(false);
}
return response;
}
The above code works fine if isCollection()
returns false and the method only has to come up with a single deserialized object, but it doesn't work for collections as I can't put the result from getResourceClass()
in the <>
from the List. That results in Identifier expected
. How can I approach this so I achieve the desired result?
Upvotes: 1
Views: 1306
Reputation: 21125
ArrayList<this.request.getResourceClass()>
This cannot work. Java type parameters must be compile-time "constants" (I mean, something known to the compiler). So,
List<String> strings
is a valid syntax for the type parameters, but
List<some.runtime.expression.here()>
is not. Next thing, I would strongly recommend you not to use Class<?>
to pass data type information. Note that Class<?>
holds information about a real type in your system. Consider you want to have a list of strings, a list of integers and a list of booleans types. You can't do ArrayList<String>.class
-- it's an illegal expression in Java, because actual type parameters are not a part of class information. Can you subclass ArrayList
with something like extends ArrayList<String>
and so on? You can but you shouldn't. What if the class to subclass is final
or you're going to use LinkedList
? Some sort of code bloat, isn't it?
Class
is java.lang.reflect.Type
, and Gson requires an instance of the latter to be passed to the fromJson()
method. What if you construct it yourself? Gson provides a convenient mechanism to construct java.lang.reflect.Type
and java.lang.reflect.ParameterizedType
(the latter is used for collections very intensively in Gson by the way, see more for Gson TypeTokens). The are two ways:
TypeToken.getParameterized(rawType, typeParameter1, typeParameter2)
For example, TypeToken.getParameterized(List.class, String.class).getType()
will return a ParameterizedType
instance as if it you could write List<String>.class
. This is a truly dynamic approach, and you can pass some runtime execution results to the getParameterized()
method.
TypeToken
subclassingThe trick here is that TypeToken
is an abstract class and it can be parameterized, letting Gson analyze a usually anonymous class for the actual parameters at runtime and let the getType()
method return a compile-time composed type: new TypeToken<List<String>>(){}.getType()
(subclasses can store information about their super classes parameterization, and Gson uses it at runtime). Note that getRawType
returns List.class
only - this is all just how Java generics are implemented since Class
cannot store actual type parameters (read more for Java generics and erasure).
Having this vague explanation in mind, just refactor your getDeserializedResponse()
method making a request return java.lang.reflect.Type
rather than Class<?>
. I even think that you can remove isCollection
from that method and just let Gson do it all itself: Gson can distinguish between List<String>
and List<Integer>
types.
Upvotes: 2