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

StackOverflow https://stackoverflow.com/questions/8805428

Вопрос

Цель

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

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

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

описание проблемы

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

Есть ли способ изменить приведенный ниже код, чтобы он работал, или мне нужно перепроектировать свой 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.
Это было полезно?

Решение

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

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

listener.onMessage(dynamicProxy);

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

Вы не можете использовать Generics здесь, потому что точный тип известен только во время выполнения, поэтому вам нужно использовать небезопасную актерскую роль. Необработанные типы не проверяют типы, следовательно, это работает. 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, чтобы подписать событие сообщения, вы просто говорите слушателю, что есть какой -то тип Сообщение произошло, вы должны сделать что -то, чтобы сыграть роль слушателя ... это роль сообщения ... Я не Konw, почему есть прокси для сообщения, это так удивительно ...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top