Автор оригинала: Ali Dehghani.
1. Обзор
В этом уроке мы познакомимся с токенами супертипа и посмотрим, как они могут помочь нам сохранить информацию об универсальных типах во время выполнения.
2. Стирание
Иногда нам нужно передать информацию о конкретном типе в метод . Например, здесь мы ожидаем, что Джексон преобразует массив байтов JSON в строку :
byte[] data = // fetch json from somewhere String json = objectMapper.readValue(data, String.class);
Мы сообщаем это ожидание с помощью буквального маркера класса, в данном случае String.class.
Однако мы не можем так же легко установить одно и то же ожидание для универсальных типов:
Mapjson = objectMapper.readValue(data, Map .class); // won't compile
Java стирает информацию об универсальном типе во время компиляции. Таким образом, параметры универсального типа являются всего лишь артефактом исходного кода и будут отсутствовать во время выполнения.
2.1. Овеществление
Технически говоря, универсальные типы не овеществляются в Java. В терминологии языка программирования, когда тип присутствует во время выполнения, мы говорим, что этот тип овеществлен.
Овеществленные типы в Java следующие:
- Простые примитивные типы, такие как long
- Неродовые абстракции, такие как String или Runnable
- Необработанные типы, такие как List или HashMap
- Универсальные типы, в которых все типы являются неограниченными подстановочными знаками, такими как List или HashMap ? > ? >
- Массивы других овеществленных типов, таких как String [], int[], List[], или Map ?>[] ?>[]
Следовательно, мы не можем использовать что-то вроде Map String>.class , потому что Map String> не является овеществленным типом. String>.class
3. Токен Супертипа
Как оказалось, мы можем воспользоваться преимуществами анонимных внутренних классов в Java, чтобы сохранить информацию о типе во время компиляции:
public abstract class TypeReference{ private final Type type; public TypeReference() { Type superclass = getClass().getGenericSuperclass(); type = ((ParameterizedType) superclass).getActualTypeArguments()[0]; } public Type getType() { return type; } }
Этот класс абстрактен, поэтому мы можем выводить из него только подклассы.
Например, мы можем создать анонимную внутреннюю:
TypeReference
Конструктор выполняет следующие действия для сохранения информации о типе:
- Во – первых, он получает общие метаданные суперкласса для этого конкретного экземпляра-в этом случае общий суперкласс является TypeReference Integer>> Integer>>
- Затем он получает и сохраняет фактический параметр типа для универсального суперкласса – в этом случае это будет Map Integer> Integer>
Затем он получает и сохраняет фактический параметр типа для универсального суперкласса – в этом случае это будет Map Integer> Integer>
TypeReference> token = new TypeReference >() {}; Type type = token.getType(); assertEquals("java.util.Map ", type.getTypeName()); Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments(); assertEquals("java.lang.String", typeArguments[0].getTypeName()); assertEquals("java.lang.Integer", typeArguments[1].getTypeName());
Затем он получает и сохраняет фактический параметр типа для универсального суперкласса – в этом случае это будет Map Integer> Integer>
Этот шаблон настолько известен, что библиотеки, такие как Jackson, и фреймворки, такие как Spring, имеют свои собственные реализации. Синтаксический анализ объекта JSON в Map String> может быть выполнен путем определения этого типа с помощью маркера супертипа: String>
TypeReference> token = new TypeReference >() {}; Map json = objectMapper.readValue(data, token);
4. Заключение
В этом уроке мы узнали, как использовать токены супертипа для сохранения информации об универсальном типе во время выполнения.
Как обычно, все примеры доступны на GitHub .