簡體   English   中英

Java泛型設計問題

[英]Java generics design problem

我想通過公共消息處理器將消息調度到特定處理程序

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

服務器處理器將處理許多不同的服務器消息,它們都具有自己的子類型,成員等。這些消息中的每一個都有一個單獨的處理程序。 一些基本消息類將共享處理程序。

我的問題是如何避免在客戶端代碼中強制轉換? 我似乎無法寫出調度方法的簽名,以包括以下事實:我們知道消息處理程序的類型將為M(ServerMessageHandler),並且特定的ServerMessageHandler將由T參數化,而類型為T的消息將位於爭論清單。

編輯

我不介意addHandler方法無法獲得完全的類型安全性,我可以進行一些運行時檢查,以確保實施了正確的關系(不過,我必須更改其簽名才能正確執行此操作)。 我這里的主要目標是以某種方式(通過簽名)強制執行dispatch方法中的兩個關系。 被調用的處理程序的類型為M,並且由T.對其進行參數化以實際調用此方法,則run方法中將有一些未經檢查的強制轉換(依次調用dispatch)。 但我不介意那里有丑陋之處。 只是嘗試將其移出ServerProcessor。

Processor.dispatch可以采用擴展MessageHandler任何類型。

ServerProcessor的方法不是完整的重寫Processor.dispatch對於不是具有類ServerMessageHandler異常的ServerMessageHandler實例的處理程序,它將失敗(我假設ServerMessageHandler不擴展MessageHandler是一個錯字,而不是設計ServerMessageHandler ;否則,它將由於所有MessageHandler都不是ServerMessageHandler ,因此對於所有輸入都將失敗;否則,只需將參數的類型ServerMessageHandler<T>

您為什么期望有一種以類型安全的方式表達本質上不安全的行為的方法?

Processor.dispatch的約定是H可以是任何MessageHandlerT可以是任何消息。 如果H只能是Processor的類型由M參數化,則在定義中使用它:

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

但是,這又再次失去了一些東西,因為M與T不相關。在Java中沒有等同於unbind / bind習慣用法的東西,而且看來dispatch方法也不應該在乎消息的子類型,還是處理器應該關心-您似乎通過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 ServerMessageHandler<T> ProcessorDispatcher意味着只有特定的Dispatcher才需要了解TServerMessageHandler<T>之間的關系。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM