javagenericsgsonerasure

How does Gson TypeToken work?


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?


Solution

  • It's a trick!

    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.