public static void main(String[] args) {
String tr = "[{\"key\":\"foo\"}, {\"key\":\"shoe\"}]";
List<MyClass> o = new Gson().fromJson(tr, getType());
for (MyClass object : o) {
System.out.println(object);
}
}
public static <T extends MyClass> Type getType() {
return new TypeToken<List<T>>() {
}.getType();
}
public static class MyClass {
private String key;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
@Override
public String toString() {
return "X{" +
"key='" + key + '\'' +
'}';
}
}
I am trying to understand how Gson works with Generics. I am getting error in above main method.
Exception in thread "main" java.lang.ClassCastException: class com.google.gson.internal.LinkedTreeMap cannot be cast to class org.example.Main$MyClass (com.google.gson.internal.LinkedTreeMap and org.example.Main$MyClass are in unnamed module of loader 'app')
Due to Type Erasure shouldn't T be set to MyClass in the method getType(). So, Gson should have created an instance of List<MyClass> instead of List<Object>? Why T is getting treated as an Object here instead of MyClass?
I tried runnig this code but it is giving me error.
You are right that this behavior does not match normal Java type erasure. However, Gson does actually see the type variable
Tand then itself performs (incorrect) type erasure on it. Gson currently ignores the bounds of type variables and always usesObjectinstead, see issue 2563.This leads to the exception you are seeing: Gson deserializes the value as
Object, and because it is a JSON object Gson creates aMap, more specifically an instance of its internalLinkedTreeMapclass.As side note, your code contains two non type-safe actions:
Creating a
TypeToken<List<T>>As you mention at runtime due to type erasure Gson cannot know the actual type of
T. So if your example had aMySubclass extends MyClass, then even if Gson's type erasure was correct you could still see aClassCastExceptionwhen you call<MySubclass>getType(). Because at runtime this information is not available so it would actually be aTypeToken<List<T extends MyClass>>, erased toTypeToken<List<MyClass>>instead ofTypeToken<List<MySubclass>>.Due to that future Gson versions will disallow capturing type variables by default.
Using
Gson.fromJson(..., Type)These methods cannot enforce type-safety and allow you to write something like the following without causing any compilation warning or error:
(note the mismatch
MyClassvs.WrongClass)You should prefer the type-safe
Gson.fromJson(..., TypeToken<T>)overloads instead, added in Gson 2.10. That is, simply omit theTypeToken.getType()call.A type-safe variant of your
getType()method could look like this:In Kotlin your original example could also work if you make the type variable
reifiedand change the return type fromTypetoTypeToken<List<T>>.