2011-11-29 7 views
5

Ho codice che ha un Map di gestori (messaggi). Sto cercando di far generare i gestori (come visto dall'interfaccia Handler). Senza generici i gestori hanno tutti bisogno di lanciare da Object alla rispettiva classe, il che sarebbe bello da evitare (ma tutto funziona). Per ogni classe di messaggio (Foo di seguito) ho una classe di gestori.Generici Java: l'acquisizione non può essere applicata all'oggetto

Come posso mantenere una mappa di qualsiasi tipo di classe per qualsiasi tipo di gestore e ottenere/chiamare con "solo" un Object? (il parametro su handleMessage(Object) non può essere limitato)

Vedere MWE di seguito.

import java.util.*; 
public class Logic 
{ 
    Map<Class<?>, Handler<?>> handlers = new HashMap<Class<?>, Handler<?>>(); 

    public void run() 
    { 
     handlers.put(Foo.class, new FooHandler()); 
    } 

    public void handleMessage(Object msg) 
    { 
     Handler<?> handler = handlers.get(msg.getClass()); 
     if (handler != null) { 
      handler.execute(msg); 
     } 
    } 

    private interface Handler<T> 
    { 
     public void execute(T msg); 
    } 

    private class FooHandler implements Handler<Foo> 
    { 
     public void execute(Foo msg) {} 
    } 

    private class Foo {} 
} 

Questo codice produce:

Logic.java:16: execute (cattura # x di?) In Logic.Handler non può essere applicato> a (java.lang.Object) gestore. eseguire (msg);

Come può essere riparato per funzionare mantenendo l'interfaccia Handler generica?

risposta

6

Non è possibile definire la relazione tra la chiave e il valore in un campo, ma è possibile utilizzare i metodi di accesso per applicarlo, purché solo questi metodi siano utilizzati per accedere alla mappa.

private final Map<Class, Handler> handlers = new HashMap<Class, Handler>(); 

public <T> void addHandler(Class<T> clazz, Handler<T> handler) { 
    handlers.put(clazz, handler); 
} 

@SuppressWarnings("unchecked") 
public <T> Handler<T> getHandler(Class<T> clazz) { 
    return (Handler<T>) handlers.get(clazz); 
} 

@SuppressWarnings("unchecked") 
public <T> Handler<T> getHandlerFor(T t) { 
    return getHandler((Class<T>) t.getClass()); 
} 

public void run() { 
    addHandler(Foo.class, new FooHandler()); 
} 

public <T> void handleMessage(T msg) { 
    Handler<T> handler = getHandlerFor(msg); 
    if (handler != null) { 
     handler.execute(msg); 
    } 
} 
+0

La tua risposta mi ha portato a digitare contenitori eterogenei sicuri, relazioni per tipi di generici e quant'altro! Sono un po 'deluso dal fatto che sia impossibile senza i metodi wrapper e @SuppressWarnings comunque: <. Tuttavia, funziona ora, ma non so se valga la pena di introdurre qui i farmaci generici. –

+0

Il problema è quanto complicato sia la sintassi generica. ;) Controlla le chiamate ad addHandler, hetHandler e getHandlerFor sono corretti. –

2

Il problema è che execute() richiede un certo tipo di parametro, che è più specifico rispetto Object.

Tuttavia, nel proprio metodo handleMessage(), il compilatore non sa quale sia il parametro. Supponiamo che un caso in cui sia registrato FooHandler per la classe Bar (che sarebbe possibile).

In tale contesto handler.execute(msg); sarebbe effettivamente causare FooHandler#execute(Foo) chiamato con un argomento Bar, che comporterebbe un ClassCastException (salvo Bar extends Foo). Quindi il compilatore rifiuta di compilare quel codice.

+0

Buon punto lì! –

1

Un'altra risposta che non era qui ma dovrebbe essere: rimuovere tutta la sintassi generica (ad esempio rimuovere tutti i <?>). Quindi il parser tornerà alla sintassi JDK1.4 e tutto funzionerà correttamente.

Problemi correlati