简体   繁体   中英

Is it possible to avoid this unchecked cast?

I'm writing a simple event system for parameterized events, which uses a Map from classes to Set s of handlers which have that class as their parameter. My understanding is that I can't define that relationship between key and value types via parameter restrictions and I'm getting unchecked cast warnings pulling elements out of the sets.

Here's what I have currently:

public class Manager {
    private class Event<T> {
        // getSubject(), etc.
    }

    private interface Handler<T> {
        public T handleEvent(Event<T> event, T subject);
    }

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

    public <T> void post(final Event<T> event, final T subject) {
        final Class<?> type = subject.getClass();

        if (this.handlers.containsKey(type)) {
            for (Handler<?> handler : this.handlers.get(type)) {
                // unchecked cast
                ((Handler<T>) handler).handleEvent(event, subject);
            }
        }
    }

    public <T> void register(final Class<T> type, final Handler<T> handler) {
        if (!this.handlers.containsKey(type)) {
            this.handlers.put(type, new HashSet<Handler<?>>());
        }

        this.handlers.get(type).add(handler);
    }
}

Is it possible to avoid this unchecked cast? Is there perhaps a flaw in my design?

I've spent quite some time here and on Google, but was unable to find anything which quite covered this arrangement.

If the event classes themselves form a hierarchy, you can avoid casts by using a Visitor pattern. It's extremely cumbersome in Java, but there are no casts necessary.

Unfortunately, you can do nothing more than @SuppressWarnings("unchecked") in this situation.

There's nothing in your map declaration that keeps you from associating a Class<A> with a Set<Handler<B>> , so the compiler can't possibly know whether this is the case or not. And sadly, there's no way to impose such a constraint on any implementation of java.util.Map .

If you were to implement your own data structure that allows this enforcement (providing methods like Handler<T> getHandler(Class<T> type) , you would only end up burying the unchecked cast in that data structure and keep your client code clean.

As a side note, the Map interface expects an Object as a parameter for get() , containsKey() and alike, so all the generic stuff gets lost anyway (even if you could associate the generic type of the key with the one of its corresponding value, which is simply not possible).

My code organization is a little different, Code for Broadcasters, the equivalent of your Manager, here but in my code for post() I do the equivalent of

for (Handler<T> handler : this.handlers.get(type)) {
   do stuff ...
}

Since the event argument is parameterized by T, you can add the <T> to the Handler. But I do a lot of the fancy Generics stuff by guessing and blundering, YMMV... :-)

ps your register() method should be synchronized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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