How to create a type safe factory for generic classes?

Viewed 464

[Note: I have looked at all the possible duplicates - none are direct duplicates and none provide an answer to this specific question.]

I want to create a factory for event handlers, where the event handler's generic type parameter is the event type. The event type is only known at runtime.

In code, this is an illustration of what I want to achieve:

public interface Event {
}

public interface EventHandler<T extends Event> {
    void handle(T event);
}

public class EventHandlerFactory {
    public <T extends Event> EventHandler<T> getHandler(Class<T> eventType) {
        return ... // implementation?
    }
}

And then be able to consume the events in a type safe way, like so:

Event event = getEvent("some json representation");
factory.getHandler(event.getClass()).handle(event);

Any ideas how I can achieve this (or even something similar if this is not possible)?

Edit:

A concrete event handler might look like this:

public class JobCreatedEventHandler implements EventHandler<JobCreatedEvent> 
{
    @Override
    public void handle(JobCreatedEvent event) {
        ...
    }
}
4 Answers

A solution using the visitor pattern (as requested in the comments):

public interface Event {
    ...
    default void accept(EventHandlerFactory factory) {
        factory.defaultHandle(this);
    }
}

public class EventA implements Event {
    public void accept(EventHandlerFactory factory) {
        factory.handleA(this);
    }
}

public class EventB implements Event {
    public void accept(EventHandlerFactory factory) {
        factory.handleB(this);
    }
}
...

public class EventHandlerFactory {
    public void handleEvent(Event e) {
        e.accept(this);
    }

    public void handleA(EventA e) {
        this.handlerA.handle(e);
    }

    public void handleB(EventB e) {
        this.handlerB.handle(e);
    }
    ...

    public void defaultHandle(Event e) {
        LOG.error("Handler method not defined for" + e.getClass().getSimpleName());
    }
}

Notes:

  • This solution works fine if the set of event types is fixed (and thus, completely inflexible with respect to subclassing)
  • You should think of organizing the classes/packages/interfaces in such a way as to reduce the visibility of handleA, handleB etc. to the actual client classes of EventFactory
  • The default Event.accept method is not strictly required; in your use case, I don't think there's a sensible implementation of EventHandlerFactory.defaultHandle
  • Of course, you could make the Event.handle methods to return the call to an appropriate getter method, e.g. return factory.getEventHandlerForB, if you require that type of indirection

This solution is entirely inflexible (pretty much because the visitor pattern is about the least flexible pattern ever invented), but it gets the job done. Note that no single casting/lookup by class is required.

Related