Java: ограничения динамического типа

В настоящее время я работаю над обработчиком протокола для своего проекта на Java. Я пытаюсь создать поддерживаемый и расширяемый API, то есть я не хочу просто жестко кодировать поддерживаемые типы значений.

Я начал разрабатывать «обработчик протокола», который при заданном значении (поддерживаемого типа) может кодировать это значение в соответствии со спецификацией протокола, не беспокоя клиента о деталях процесса перевода.

Различные типы значений, например Строки или целые числа обязательно имеют разные процессы кодирования, но я не хочу, чтобы клиенты беспокоились о хранении ссылок на другой объект для каждого возможного типа, который им может потребоваться кодировать - как я уже сказал, я не хочу, чтобы они беспокоились о деталях.

До сих пор я определил «общий» класс DynamicHandler, который поддерживает коллекцию «определенных» типов StaticHandler:

class DynamicHandler
{
  Map<Class, StaticHandler> handlers;

  <T> void handle(T value)
  {
    if (handlers.containsKey(value.class))
      handlers.get(value.getType()).handle(value);
  }

  void <T> register(StaticHandler<T> handler)
  {
    handlers.put(T.class, handler);
  }
}

Идея этого класса заключается в том, что клиент просто передает значение, которое он хочет закодировать, методу handle, а DynamicHandler ищет и делегирует StaticHandler.

interface StaticHandler<T>
{
  void handle(T value);
}

Вот пример клиента, который использует эту систему:

class StringHandler implements StaticHandler<String>
{
  void handle(String value)
  {
    ...
  }
}

DynamicHandler handler = new DynamicHandler();
handler.register(new StringHandler());
handler.handle("Hello World!");

У меня есть два вопроса, на которые я пытаюсь найти ответ самостоятельно:

  1. Как в методе DynamicHandler.register получить тип T, не имея экземпляра T?
  2. Можно ли реализовать тип DynamicHandler как java.util.Map, чтобы максимизировать совместимость с любыми сторонними клиентами кода, которые могут использоваться для создания или иной обработки таких объектов?

Обновлено: поскольку DynamicHandler по сути является картой, хотя и с некоторыми общими уловками, можно ли реализовать ее как: DynamicHandler implements java.util.Map<...,...> (я не совсем уверен, какие типы ключей и значений должны быть здесь).

Я впервые задаю здесь вопрос, поэтому я надеюсь, что я был достаточно ясен для всех вас. Если есть что-то, что, по вашему мнению, требует разъяснения, просто дайте мне знать, и я постараюсь изо всех сил.

Не могли бы вы более четко сформулировать свой второй вопрос? Я изо всех сил пытаюсь понять, что именно вы здесь спрашиваете.

OhleC 08.11.2018 10:30

Вы можете вызвать getClass() для объекта, переданного в DynamicHandler.handle(), вместо использования obj.class, который не компилируется. Вы предполагаете, что если null передан, не должно быть ничего, что вам нужно было бы обрабатывать, и не будет никаких шансов, что объект соответствует нескольким ключам из карты.

Jai 08.11.2018 10:38

@Jal Мои извинения, на самом деле это была ошибка в моем коде. Я обновил его, чтобы вместо этого использовать getClass (). Я также прояснил свой первый вопрос, чтобы прояснить, что хочу знать, как получить тип T в методе регистрации.

Matt Mead 08.11.2018 10:45

@Ohlec Я отредактировал свой второй вопрос, надеюсь, теперь он стал яснее.

Matt Mead 08.11.2018 10:45

@MattMead, как вы представляете клиента, использующего это как карту? Разве handle() не самый удобный интерфейс для этого?

OhleC 08.11.2018 10:56

@Ohlec Я подозреваю, что вы правы в том, что register () и handle () достаточны для моих предполагаемых случаев использования. Я представлял себе что-то вроде register () эквивалентным put (), например. handlerMap.put (String.class, new StringHandler ()) и handle () эквивалентны get (), например. handlerMap.get (String.class). Однако общие параметры для handle () и register () несовместимы с Map.put () и get (). Я думаю, что мне, вероятно, следует реализовать класс как есть на данный момент и при необходимости побеспокоиться об обобщении на Map позже.

Matt Mead 08.11.2018 11:10
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

  1. In the DynamicHandler class, how can I get the type of T without having an instance of T?

Обычное решение для этого - передать ему объект Class:

 void <T> register(StaticHandler<T> handler, Class<T> clazz)
  {
    handlers.put(clazz, handler);
  }

Как один из примеров этого в широко используемой библиотеке: Gson делает что-то подобное для регистрации сериализаторов JSON для определенных типов с помощью GsonBuilder.registerTypeHierarchyAdapter.

Или с той же идеей добавьте метод в StaticHandler, который дает класс T

Sodala 08.11.2018 10:34

@ohlec Спасибо, я сам задавался вопросом об этом, но не был уверен, был ли это лучший или самый распространенный способ сделать это. Для клиента показалось немного избыточным явно указывать тип в качестве параметра, если он неявно указан из T. Я постараюсь сделать это таким образом.

Matt Mead 08.11.2018 10:35

@Sodala Меня тоже интересовал этот подход. Я попробую оба и посмотрю, что мне больше подходит.

Matt Mead 08.11.2018 10:37

@MattMead, безусловно, выглядит избыточным, но из-за стирания типа клиентам действительно нужно явно указывать тип каким-то образом (погода в регистре или в другом методе на самом деле не имеет значения)

OhleC 08.11.2018 10:40

@MattMead Я добавил ссылку на пример, в котором Google GSON использует именно этот шаблон для очень похожей цели.

OhleC 08.11.2018 10:52

@Ohlec Спасибо, что добавили это, похоже, это будет полезно для меня!

Matt Mead 08.11.2018 11:02
Ответ принят как подходящий

Если значения null не обрабатываются и он спроектирован таким образом, что никогда не будет никакого значения, принадлежащего нескольким классам, вы можете сделать это:

public class DynamicHandler {
    Map<Class, StaticHandler> handlers;

    public <T> void handle(T value) {
        if (value != null) {
            handlers.entrySet()
                    .stream()
                    .filter(entry -> 
                        entry.getKey().isInstance(value))
                    .findAny()
                    .ifPresent(entry ->
                        entry.getValue().handle(value));
        }
    }

    public void <T> register(StaticHandler<T> handler) {
        handlers.put(handler.getHandlingClass(), handler);
    }
}


interface StaticHandler<T>
{
    void handle(T value);
    Class<T> getHandlingClass();
}

public class StringHandler implements StaticHandler<String> {
    @Override public void handle(String value) {
        ...
    }
    @Override public final Class<String> getHandlingClass() {
        return String.class;
    }
}

@Jal В настоящее время я планирую игнорировать нулевые значения, кажется, нет смысла ничего «кодировать» для моего предполагаемого варианта использования. Я думаю, что предоставленный вами код - элегантное решение моей проблемы. Я определенно пропустил идею getHandlingClass () для статического обработчика, спасибо!

Matt Mead 08.11.2018 11:18

@MattMead Когда дело доходит до разработки API, определенно нужно учесть много вещей. У каждого подхода есть свои плюсы и минусы. Лучше знать о различных подходах, а затем рассматривать их оттуда.

Jai 09.11.2018 02:43

Другие вопросы по теме