2009-09-30 88 views
0

我想通过一个共同的信息处理器Java泛型的设计问题

// 
// Library code 
// 

abstract class Processor<M extends MessageHandler<? extends Message>> { 
    HashMap<Class<Message>, M> handlerMap; 
    void addHandler(M, Class<Message>); 
    void run() { 
     while(true) { 
      ... 
     } 
    } 
    // QUESTION - how to define this to include the fact that H extends M<T> 
    //   actually im just trying to avoid the ugly cast in the client code. 
    abstract <H extends MessageHandler<T>, T extends Message> void dispatch(H handler, T message); 
} 

class MessageHandler<T extends Message> { 
} 

class Message { 
} 

// 
// Client code 
// 

class ServerMessage extends Message { 
    ... 
} 

class ServerMessageHandler<T extends Message> extends MessageHandler<T> { 
    ... 
    void process(T msg, Object... params) { 
     ... 
    } 
} 

class ServerProcessor extends Processor<ServerMessageHandler<? extends Message>> { 
    @Override 
    <H extends MessageHandler<T>, T extends Message> void dispatch(H handler, T message) { 
     // QUESTION - how do i get rid of this cast? 
     ((ServerMessageHandler<T>)handler).process(T, ...); 
    } 
} 

服务器处理器将处理许多不同的服务器消息分派消息特有的处理程序,都用自己的亚型,成员等每一个这些消息将有一个单独的处理程序。一些基类消息类将共享处理程序。

我的问题是我如何避免在客户端代码丑陋的投射?我似乎不能编写dispatch方法的签名来包含事实,我们知道消息处理程序将是M类型(ServerMessageHandler),并且特定的ServerMessageHandler由T参数化,并且类型T的消息将在争论名单。

编辑

我不介意,如果addHandler操作方法不能得到总的类型安全,我可以做一些运行时检查,以确保适当的关系被强制执行(我将不得不改变其签名做正确虽然)。我的主要目标是以某种方式强制(通过签名)调度方法中的两种关系。被调用的处理程序是M类型的,并且被T参数化以实际调用此方法,run方法中将会有一些未经检查的强制转换(它依次调用dispatch)。但我不介意在那里有丑陋。只是试图将其移出ServerProcessor。

+0

我可以写 摘要 void dispatch(M handler,T message) 但这需要在代码 – aepurniet 2009-09-30 09:52:45

回答

3

Processor.dispatch可以采取延伸MessageHandler的任何类型。

ServerProcessor的方法是不完整的替代Processor.dispatch - 它会失败处理不属于ServerMessageHandler用类转换异常(我假设ServerMessageHandler不延长MessageHandler是一个错字,而不是由设计实例;否则它将会失败,因为没有MessageHandlerServerMessageHandler;否则只是使参数的类型为ServerMessageHandler<T>)。

为什么你会期望有一种方式来表达内在不安全的行为以类型安全的方式?

合同Processor.dispatchH可以是任何MessageHandlerT可以是任何消息。如果H而不是只能处理Processor的类型由,M parametrised,然后使用该定义:

abstract class Processor<M extends MessageHandler<? extends Message>> { 
    ... 
    abstract <T extends Message> void dispatch (M handler, T message); 
} 

但同样,这失去在于M的东西是不相关的T.有ISN”在Java中,任何与解绑定/绑定成语相当的东西,似乎派遣方法或者不应该关心消息的子类型,或者处理器应该关心 - 你似乎正在维护混合的消息处理类型addHandler方法在运行时为任何方法提供处理程序,然后希望在调度方法中使其特定于特定类型。

那么处理器是否只处理一种消息类型呢?如果确实如此,并且需要类型安全性,则将消息类型设置为类型参数。如果它处理在运行时决定的多个消息类型,那么您将不会得到该类型的编译时检查。


如果你分开的事件处理循环,并派遣一个处理机构可以移动施法:

/** 
* @param <M> message type 
*/ 
class Processor <M> { 

    Dispatcher<M> dispatcher; 

    public Processor (Dispatcher<M> dispatcher) { 
     this.dispatcher = dispatcher; 
    } 

    void run (M...messages) { 
     for (M message : messages) { 
      // as there is no mechanism in java to get from Class<T> to Foo<T>, this call 
      // must be made with the wildcard H Foo<?> 
      dispatcher.dispatch (message); 
     } 
    } 
} 

interface Dispatcher<M> { 
    <T extends M> void dispatch (T message); 
} 

class Message { 
} 

class ServerMessage extends Message { 
    //... 
} 

interface ServerMessageHandler<T extends ServerMessage> { 
    //... 
    void process (T msg, String param) ; 
} 

class ServerDispatcher implements Dispatcher<ServerMessage > { 
    HashMap < Class < ? extends ServerMessage >, ServerMessageHandler<?> > handlerMap = new 
    HashMap < Class < ? extends ServerMessage >, ServerMessageHandler<?> >(); 

    <T extends ServerMessage > 
    void addHandler (ServerMessageHandler<T> handler, Class <T> clz) { 
     handlerMap.put (clz, handler); 
    } 

    @SuppressWarnings("unchecked") 
    // cannot use a trick like clz.cast() as we want ServerMessageHandler<T> rather than T 
    <T extends ServerMessage> ServerMessageHandler<T> getHandler (Class < ? extends ServerMessage > clz) { 
     return (ServerMessageHandler<T>) handlerMap.get(clz); 
    } 

    @Override 
    public <T extends ServerMessage> 
    void dispatch (T message) { 
     ServerMessageHandler<T> serverMessageHandler = getHandler (message.getClass()); 

     serverMessageHandler.process (message, "wibble"); 
    } 
} 

但是,如果环路自基本事件类型的队列驱动如Process.run()代码中那样,在类型安全性方面它将不会更好,因为调用的ServerDispatcher.dispatch的唯一版本是T = ServerMessage,并且演员被隐藏在getHandler()方法中。安全来自addHandlergetHandler的对称性,而不是来自类型变量T的不同绑定。将ProcessorDispatcher分开意味着只有特定的Dispatcher必须知道TServerMessageHandler<T>之间的关系。

+0

的客户端更多的强制转换错误。编辑 – aepurniet 2009-09-30 10:24:48

+0

它可能有点不安全,但异形类型安全容器模式给了我一点希望它可以完成。即时通讯试图让暴露的接口功能类型安全,而下面有一些演员让它工作。即时通讯只是试图将这些强制转换为库代码。 – aepurniet 2009-09-30 10:38:05

+0

修复了addHandler方法中的错字。也删除了ServerMessageHandler类中不必要的特化,T应该只扩展Message。 – aepurniet 2009-09-30 10:47:43