Как работает Gson TypeToken?

Я понимаю, что в Java, в отличие от, например, С# generics являются функцией компиляции и удаляются с помощью стирания типа. Итак, как работает Gson TypeToken? Как он получает общий тип объекта?

Ответы

Ответ 1

It's a trick!

Из § 4.6 JLS (выделено мной):

Тип erasure - это отображение типов (возможно, включая параметризованные типы и переменные типа) для типов (которые никогда не являются параметризованными типами или переменными типа). Будем писать | T | для стирания типа Т. Отображение стирания определяется следующим образом:

Стирание параметризованного типа (§4.5) G является | G |.

Стирание вложенного типа T.C является | T |.C.

Стирание типа массива T [] равно | T | [].

Стирание переменной типа (§4.4) является стиранием ее левой части.

Стирание любого другого типа - это сам тип.

Следовательно, если вы объявляете класс с анонимным подклассом самого себя, он сохраняет его параметризованным типом; он не стирается. Поэтому рассмотрим следующий код:

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()));
    }
}

Выводится:

null
class Erasure
[java.util.HashMap<java.lang.Integer, java.lang.String>]

Обратите внимание, что вы получите ClassCastException, если вы не объявили класс анонимно, из-за стирания; суперкласс не будет параметризованным типом, это будет Object.

Ответ 2

Стирание типа Java применяется к отдельным объектам, а не к классам или полям или методам. TypeToken использует анонимный класс, чтобы обеспечить сохранение общей информации типа, а не просто создание объекта.