Рубрики
Без рубрики

Токены супертипа в Java-генераторах

Узнайте, как сохранить информацию об универсальном типе во время выполнения с помощью токенов супертипа.

Автор оригинала: Ali Dehghani.

1. Обзор

В этом уроке мы познакомимся с токенами супертипа и посмотрим, как они могут помочь нам сохранить информацию об универсальных типах во время выполнения.

2. Стирание

Иногда нам нужно передать информацию о конкретном типе в метод . Например, здесь мы ожидаем, что Джексон преобразует массив байтов JSON в строку :

byte[] data = // fetch json from somewhere
String json = objectMapper.readValue(data, String.class);

Мы сообщаем это ожидание с помощью буквального маркера класса, в данном случае String.class.

Однако мы не можем так же легко установить одно и то же ожидание для универсальных типов:

Map json = 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> token = new 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 .