简体   繁体   English

Java泛型设计问题

[英]Java generics design problem

i want to dispatch messages to specific handlers through a common message processor 我想通过公共消息处理器将消息调度到特定处理程序

//
// 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, ...);
   }
}

the server processor will be processing many different server messages, all with their own subtypes, members, etc. each one of these messages will have a separate handler. 服务器处理器将处理许多不同的服务器消息,它们都具有自己的子类型,成员等。这些消息中的每一个都有一个单独的处理程序。 some base message classes will share handlers. 一些基本消息类将共享处理程序。

my question is how do i avoid that ugly cast in the client code? 我的问题是如何避免在客户端代码中强制转换? i cant seem to write the signature of the dispatch method to include the facts that we know the message handlers will be of type M (ServerMessageHandler), and that the particular ServerMessageHandler is parameterized by T, and a message of type T will be in the arguement list. 我似乎无法写出调度方法的签名,以包括以下事实:我们知道消息处理程序的类型将为M(ServerMessageHandler),并且特定的ServerMessageHandler将由T参数化,而类型为T的消息将位于争论清单。

EDIT 编辑

i dont mind if the addHandler method cannot get total type safety, i can do some runtime checks to make sure the proper relationships are enforced ( i would have to change its signature to do that properly though ). 我不介意addHandler方法无法获得完全的类型安全性,我可以进行一些运行时检查,以确保实施了正确的关系(不过,我必须更改其签名才能正确执行此操作)。 my main goal here is to somehow enforce (through the signature) the two relationships in the dispatch method. 我这里的主要目标是以某种方式(通过签名)强制执行dispatch方法中的两个关系。 that the handler being called is of type M, and that it is parameterized by T. to actually call this method there will be some unchecked casts in the run method (which in turn calls dispatch). 被调用的处理程序的类型为M,并且由T.对其进行参数化以实际调用此方法,则run方法中将有一些未经检查的强制转换(依次调用dispatch)。 but i dont mind having the ugliness there. 但我不介意那里有丑陋之处。 just trying to move it out of the ServerProcessor. 只是尝试将其移出ServerProcessor。

Processor.dispatch can take any type which extends MessageHandler . Processor.dispatch可以采用扩展MessageHandler任何类型。

The method in ServerProcessor isn't a complete override Processor.dispatch - it will fail for handlers which aren't ServerMessageHandler instances with a class cast exception ( I'm assuming that ServerMessageHandler not extending MessageHandler is a typo rather than by design; otherwise it will fail for all inputs as no MessageHandler is a ServerMessageHandler ; otherwise just make the parameter's type ServerMessageHandler<T> ). ServerProcessor的方法不是完整的重写Processor.dispatch对于不是具有类ServerMessageHandler异常的ServerMessageHandler实例的处理程序,它将失败(我假设ServerMessageHandler不扩展MessageHandler是一个错字,而不是设计ServerMessageHandler ;否则,它将由于所有MessageHandler都不是ServerMessageHandler ,因此对于所有输入都将失败;否则,只需将参数的类型ServerMessageHandler<T>

Why would you expect there to be a way of expressing intrinsically unsafe behaviour in a typesafe manner? 您为什么期望有一种以类型安全的方式表达本质上不安全的行为的方法?

The contract for Processor.dispatch is that H can be any MessageHandler and T can be any message. Processor.dispatch的约定是H可以是任何MessageHandlerT可以是任何消息。 If H instead can only be the type of handler Processor is parametrised by, M , then use that in the definition: 如果H只能是Processor的类型由M参数化,则在定义中使用它:

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

But again, this loses something in that M isn't related to T. There isn't anything equivalent to the unbind/bind idiom in Java, and it does seem that the dispatch method either shouldn't care about the subtype of message, or the processor should care - you seem to be maintaining a mix of message handler types by your addHandler method taking a handler for any method at runtime, then wanting to make it specific for a particular type in the dispatch method. 但是,这又再次失去了一些东西,因为M与T不相关。在Java中没有等同于unbind / bind习惯用法的东西,而且看来dispatch方法也不应该在乎消息的子类型,还是处理器应该关心-您似乎通过addHandler方法维护消息处理程序类型的混合,从而在运行时为任何方法获取一个处理程序,然后希望将其特定于调度方法中的特定类型。

So does a processor handle only one message type or not? 那么处理器是否只处理一种消息类型? If it does, and you want type safety, then make the message type a type parameter. 如果是这样,并且您想键入安全性,则使消息键入一个类型参数。 If it handles multiple message types decided at runtime, then you won't get compile time checking of the type. 如果它处理在运行时确定的多种消息类型,则不会获得该类型的编译时检查。


You can move the cast if you separate the event processing loop and the mechanism to dispatch to a handler: 如果您将事件处理循环和分配给处理程序的机制分开,则可以移动演员表:

/**
* @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" );
    }
}

however, if the loop is driven from a queue of the base event type as in the Process.run() code, it will be no better in terms of type safety, as the only version of ServerDispatcher.dispatch called is with T = ServerMessage , and the cast is hidden in the getHandler() method. 但是,如果像Process.run()代码那样从基本事件类型的队列中驱动循环,则在类型安全方面并不会更好,因为调用的唯一ServerDispatcher.dispatch版本是T = ServerMessage ,并且类型转换隐藏在getHandler()方法中。 Safety comes from the symmetry of addHandler and getHandler , not from different bindings of the type variable T . 安全来自的对称性addHandlergetHandler ,而不是从类型的变量不同的绑定T Separating Processor and Dispatcher means only the specific Dispatcher has to know about the relationship between T and ServerMessageHandler<T> . ServerMessageHandler<T> ProcessorDispatcher意味着只有特定的Dispatcher才需要了解TServerMessageHandler<T>之间的关系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM