Избегание необработанных типов в диспетчере сообщений Java


Цель

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

Ожидается, что пользователь:

  1. определите интерфейс для каждого типа пользовательского сообщения.
  2. регистрируйте прослушиватель в диспетчере сообщений для каждого типа сообщений.
  3. передайте необработанные / сторонние данные диспетчеру сообщений.
  4. обработка сообщений передается слушателям.

Описание Задачи

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

Есть ли способ изменить приведенный ниже код, чтобы он работал, или мне нужно перепроектировать мой API?

Интерфейсы

MessageDispatcher реализует следующее интерфейс:

public interface MessageDispatcher {

    // Register a listener for a given user defined message type.
    public <T> void registerListener(
        Class<T> messageClass, 
        MessageListener<T> listener);

    // Receive data in 3rd party format, convert and dispatch.
    public void onData(Data data);

}

Интерфейс MessageListener определяется следующим образом:

public interface MessageListener<T> {

    public void onMessage(T message);   

}

Пример сообщения пользователя может выглядеть следующим образом:

public interface MyMessage {

    public String getName();   

}

Регистрация Слушателей

Пользователь может зарегистрировать слушателя следующим образом:

messageDispatcher.registerListener(MyMessage.class, 
    new MessageListener<MyMessage.class>() {
    @Override

   public void onMessage(MyMessage message) {
        System.out.println("Hello " + message.getName());
    }
}

Стандартный диспетчер сообщений может реализовать такой метод:

private Map<Class<?>,MessageListener<?>> messageClassToListenerMap;

public <T> void registerListener(
    Class<T> messageClass, 
    MessageListener<T> listener) {

    messageClassToListenerMap.put(messageClass, listener);

    // SNIP: Process the messageClass and extract the information needed
    // for creating dynamic proxies elsewhere in a proxy factory.

}

Отправка Сообщений

Когда MessageDispatcher получает новое сообщение, он создает динамический прокси-сервер для объекта и отправляет его к подходящему слушателю. Но вот в чем моя проблема:

public void onData(Data data) {

    // SNIP: Use proxy factory (not shown) to get message class and
    // dynamic proxy object appropriate to the 3rd party data.
    Class<?> messageClass;  // e.g. = MyMessage.class;
    Object dynamicProxy;    // e.g. = DynamicProxy for MyMessage.class;

    // TODO: How to I pick the appropriate MessageListener and dispatch the
    // dynamicProxy in a type safe way?  See below.

}

Если я попытаюсь использовать тип, я не смогу отправить данные:

// Assuming a listener has been registered for the example:
MessageListener<?> listener = messageClassToListenerMap.get(messageClass);

listener.onMessage(dynamicProxy); // ERROR: can't accept Object.
listener.onMessage(messageClass.cast(dynamicProxy); // ERROR: Wrong capture.
Это имеет смысл, потому что я никак не могу знать, какие данные принимает мой слушатель и какие данные я передаю ему.

Но если я использую необработанные типы, это прекрасно работает:

// Assuming a listener has been registered for the example:
MessageListener listener = messageClassToListenerMap.get(messageClass);  
listener.onMessage(dynamicProxy); // OK, provided I always pass the correct type of object.
3 4

3 ответа:

Вам не нужно использовать необработанные типы - просто приведите подстановочные типы В тип, который делает то, что вы хотите. Это вроде как бросает вызов безопасности типа. И это даст непроверенное предупреждение о броске, которое вы можете игнорировать. Но это доказывает, что можно не использовать сырые типы.

MessageListener<Object> listener = (MessageListener<Object>)messageClassToListenerMap.get(messageClass);

listener.onMessage(dynamicProxy);

Здесь нельзя использовать универсальные выражения, поскольку точный тип известен только во время выполнения, поэтому необходимо использовать небезопасное приведение. Необработанные типы не проверяют типы, поэтому это работает. Generics работает только во время компиляции, ваш диспетчер работает во время выполнения.

Итак, вы должны явно проверить это:

MessageListener listener = messageClassToListenerMap.get(messageClass);  
if(!messageClass.isAssignableFrom(dynamicProxy.getClass()))
  throw new Something();
listener.onMessage(dynamicProxy);

Я думаю, что это неправильная конструкция. Я бы рекомендовал сделать что-то вроде этого:

interface MyMessageListener
{
  void onMessageA(String name);
  void onMessageB(String otherParam);
}

Когда вы можете отправлять сообщения по интерфейсному классу и имени метода. (вы можете использовать интерфейсы с одним метод, но не очень приятный имхо). Более того, у весны уже есть инфраструктура для этого: MethodInterceptor, RemoteExporter, RemoteInvocation и некоторые родственные.

У меня есть trubble, когда я вижу ваш код, почему вы используете прокси для вашего класса сообщений ... сообщение-это просто Java-Боб, чтобы подписать сообщение событие произошло , вы просто говорите слушателю, что, есть сообщение некоторого типа произошло, вы должны сделать что-то, чтобы сделать роль слушателя... это роль сообщения .... я не знаю, почему есть прокси для сообщения, это так удивительно......