A TypeToken
is kind of a hack with generics. It depends on subclassing the type, either with an anonymous or normal class, and using Class#getGenericSuperclass()
which states
If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code.
In other words, in an anonymous class declaration like this
new TypeToken<ApiResponse<T>>() {}.getType())
the superclass is TypeToken<ApiResponse<T>>
. It's equivalent to
class Subclass extends TypeToken<ApiResponse<T>>
assuming T
was in scope. So when you call Class#getGenericSuperclass()
, it will return a ParameterizedType
that knows about ApiReponse<T>
since that is the actual type parameters used in the source code.
When you call your original function with any of
ApiResponse<List<JobModel>> response = ApiResponse.fromJson(new String(bytes));
ApiResponse<Double> response = ApiResponse.fromJson(new String(bytes));
ApiResponse<JobModel> response = ApiResponse.fromJson(new String(bytes));
although the compiler will infer and bind the corresponding type as a type argument to the method invocation, the internals of the method will pass the same TypeToken
object with ApiResponse<T>
. Since Gson doesn't know what T
is, it will use a default that depends on what it sees in the JSON. If it sees an object, it will use a LinkedTreeMap
. If it sees a numeric primitive, it will use the double
. Etc.
In the case where you pass a TypeToken
,
ApiResponse.fromJson(new String(bytes), new TypeToken<ApiResponse<JobModel>>() {});
it's equivalent to
class Subclass extends TypeToken<ApiResponse<JobModel>>
In other words, Class#getGenericSuperclass()
will return a ParameterizedType
that has ApiResponse<JobModel>
. Gson can extract the JobModel
and use it as a hint for deserializing the JSON.
Can anyone explain me why is that happening and is there any way to fix it ?
There's nothing really to fix. That's just how it works.
Additional reading: