I understand that in Java contrary to, for example, C# generics are compile-time feature and is removed via type erasure. So, how does Gson's TypeToken
really work? How does it get the generic type of an object?
From §4.6 of the JLS (emphasis mine):
Type erasure is a mapping from types (possibly including parameterized types and type variables) to types (that are never parameterized types or type variables). We write |T| for the erasure of type T. The erasure mapping is defined as follows:
The erasure of a parameterized type (§4.5) G is |G|.
The erasure of a nested type T.C is |T|.C.
The erasure of an array type T[] is |T|[].
The erasure of a type variable (§4.4) is the erasure of its leftmost bound.
The erasure of every other type is the type itself.
Therefore, if you declare a class with an anonymous subclass of itself, it keeps it's parameterized type; it's not erased. Therefore, consider the following code:
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.HashMap;
public class Erasure<T>
{
public static void main(String...strings) {
Class<?> foo = new Erasure<HashMap<Integer, String>>() {}.getClass();
ParameterizedType t = (ParameterizedType) foo.getGenericSuperclass();
System.out.println(t.getOwnerType());
System.out.println(t.getRawType());
System.out.println(Arrays.toString(t.getActualTypeArguments()));
}
}
This outputs:
null
class Erasure
[java.util.HashMap<java.lang.Integer, java.lang.String>]
Notice that you would get a ClassCastException
if you did not declare the class anonymously, because of erasure; the superclass would not be a parameterized type, it would be an Object
.