2011-11-29 8 views
5

Mam kod, który ma Map z (wiadomości) Handler. Próbuję zrobić generatory obsługi (jak widać przez interfejs Handler). Bez leków generycznych wszyscy opiekunowie muszą rzucać z Obiektu do odpowiedniej klasy, co byłoby fajnie uniknąć (ale wszystko działa). Dla każdej klasy wiadomości (Foo poniżej) Mam klasy obsługi.Generics Java: przechwytywanie nie może być zastosowane do Object

Jak mogę zachować mapę dowolnego rodzaju Klasy w jakimkolwiek rodzaju Handlerów i uzyskać/zadzwonić z "po prostu" Object? (nie można ograniczyć parametru do handleMessage(Object))

Zobacz poniżej MWE.

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 {} 
} 

Ten kod daje:

Logic.java:16: execute (capture # x z?) W Logic.Handler nie może być stosowany> do (java.lang.Object) obsługi. execute (msg);

Jak można to naprawić, aby działało, zachowując jednocześnie ogólny interfejs programu obsługi?

Odpowiedz

6

Nie można zdefiniować relacji między kluczem a wartością w polu, ale można użyć metod dostępu w celu jego egzekwowania, o ile tylko te metody są używane do uzyskania dostępu do mapy.

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

Twoja odpowiedź doprowadziła mnie do wpisania bezpiecznych heterogenicznych pojemników, relacji dla typów generycznych i innych! Jestem trochę zawiedziony, że nie da się tego obejść bez owijki i @SuppressWarnings: <. Mimo to działa teraz, ale nie wiem, czy to jest warte kłopotu z wprowadzeniem tutaj generycznych. –

+0

Problem w tym, jak skomplikowana jest składnia generyczna. ;) Sprawdza, czy wywołania addHandler, hetHandler i getHandlerFor są poprawne. –

2

Problemem jest to, że execute() trwa pewien typ parametru, który jest bardziej szczegółowy niż Object.

Jednak w Twojej metodzie handleMessage() kompilator nie wie, jakiego typu jest parametr. Załóżmy, że przypadek, w którym FooHandler jest zarejestrowany dla klasy Bar (co byłoby możliwe).

W tym kontekście handler.execute(msg); faktycznie spowodować FooHandler#execute(Foo) miano z Bar argumentu, który spowodowałby ClassCastException (chyba Bar extends Foo). W związku z tym kompilator odmawia skompilowania tego kodu.

+0

Dobra racja! –

1

Kolejna odpowiedź, której nie było, ale powinna być - należy usunąć całą składnię generyczną (np. Usunąć cały kod <?>). Następnie parser powróci do składni JDK1.4 i wszystko będzie działać poprawnie.

Powiązane problemy