避免Java消息调度程序中的原始类型

时间:2012-01-10 15:06:58

标签: java generics dynamic-proxy raw-types

目的

我正在尝试构建一个MessageDispatcher,它将来自第三方API的消息转换为用户定义的消息,然后将它们分派给用户注册的侦听器。

用户应该:

  1. 为每种类型的用户消息定义接口。
  2. 为每种消息类型注册一个带有消息调度程序的侦听器。
  3. 将原始/第三方数据传递给邮件调度程序。
  4. 处理传递回侦听器的消息。
  5. 问题描述

    不幸的是,我似乎无法避免使用原始类型来实现我想要的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.
    

3 个答案:

答案 0 :(得分:1)

您不能在此处使用泛型,因为只有在运行时才知道确切类型,因此您必须使用不安全的强制转换。原始类型不检查类型,因此它有效。泛型仅在编译时工作,您的调度程序在运行时工作。

所以,你应该明确检查它:

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);
}

何时可以通过接口类和方法名称分派消息。 (你可以使用单一方法的接口,但不是那么好的imho)。 此外,Spring已经拥有了基础架构:MethodInterceptorRemoteExporterRemoteInvocation以及一些相关的。

答案 1 :(得分:1)

您不需要使用原始类型 - 只需将通配类型转换为可以执行所需操作的类型。这有点类型安全。它会给出一个未经检查的投射警告,你可以忽略它。但它证明了不可能使用原始类型。

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

listener.onMessage(dynamicProxy);

答案 2 :(得分:0)

当我看到你的代码时,我有一个trubble,为什么你使用代理为你的消息类...消息只是一个java bean来签署一个消息事件发生,你只是告诉听众,有,发生了一些类型的消息,你应该做一些事情来做一个监听者的角色......这是一个消息的角色....我不知道为什么有一个消息的代理,它是如此惊喜.....