You cannot substitute the type parameter at the compile type with runtime invocations -- type information along with type parameters are generated by Java compiler at the compile time.
First off, note how Gson.fromJson(...)
overloads are declared: neither uses TypeToken
as a parameter, but they can accept Type
-- this is fine, because TypeToken
s are not mandatory to parse parameterized types JSONs, types matter only. Second, it's really fine to pass ready-to-use type Type
instances to the fromJson
method:
final Foo foo = gson.fromJson(..., Foo.class);
final List<Foo> foos = gson.fromJson(..., new TypeToken<List<Foo>>(){}.getType());
Note that in both cases literals are passed: the second one is just longer and created every time the line is executed. The Type
interface does not provide any mutator methods, so you can consider it something that can be treated as a constant (as well as other value types ilke String
s, Integer
s, and so forth are):
static final TypeToken<List<Foo>> fooListTypeToken = new TypeToken<List<Foo>>(){};
...
final List<Foo> foos = gson.fromJson(..., fooListTypeToken.getType());
Or even shorter (however I prefer the previous way -- and I would recommend it to you [you can reuse the type token and not lose it]):
static final Type fooListType = new TypeToken<List<Foo>>(){}.getType();
...
final List<Foo> foos = gson.fromJson(..., fooListTypeToken);
How much fooListTypeToken
and Foo.class
differ from the programmatic point of view? Well, essentially no difference (except that the first one represents a list whilst the latter represents a concrete class, of course). Both are values.
However, if you want to get dynamic types by a given element class (or better a type) for any must reason, then you can create Type
instances yourself and just pass them to fromJson
:
static ParameterizedType createJavaUtilListParameterizedType(final Type elementType) {
return new ParameterizedType() {
@Override
public Type getRawType() {
return List.class;
}
@Override
public Type[] getActualTypeArguments() {
return new Type[]{ elementType };
}
@Override
public Type getOwnerType() {
return null;
}
};
}
The method just creates an anonymous implementation of ParameterizedType
where:
getRawType()
- must return java.util.List.class
, a container parameterized type
getActualTypeArguments()
- must return the element type; note that it always must return a new array in order not to be damaged elsewhere (since it's not a 0-length array)
getOwnerType()
- not implemented in this case and can safely return null
.
So now the substitution is available at runtime:
final List<Foo> foos = gson.fromJson(..., createJavaUtilListParameterizedType(Foo.class));
but
final List<Foo> foos = gson.fromJson(..., createJavaUtilListParameterizedType(new TypeToken<GenericElement<String, Integer>>(){}.getType()));
for parameterized types. Well, still does not differ much from holding static final
references to type tokens and extracting the types from them later. And semantically it's the same as what the compiler does at compile time.
Edit
Gson TypeToken
also provides static TypeToken<?> getParameterized(Type rawType, Type... typeArguments)
that can be used instead of own implementation of the parameterized type like:
static ParameterizedType createJavaUtilListParameterizedType(final Type elementType) {
return (ParameterizedType) TypeToken.getParameterized(List.class, elementType).getType();
}
>(){}.getType()` instances and not `Class` instances for every particular `fromJson(...)` call.
– Lyubomyr Shaydariv Feb 14 '17 at 13:50