How to implement factory pattern with generics in Java?

user1522820 picture user1522820 · Dec 15, 2015 · Viewed 13.4k times · Source

I have a generic interface Handler

public interface Handler<T> {
  void handle(T obj);
}

I can have n implementations of this interface. Let's say I have following 2 implementations for now. One which handles String objects and another handles Date

public class StringHandler implements Handler<String> {
  @Override
  public void handle(String str) {
    System.out.println(str);
  }
}

public class DateHandler implements Handler<Date> {
  @Override
  public void handle(Date date) {
    System.out.println(date);
  }
}

I want to write a factory which will return handler instances based on the class type. Something like this :

class HandlerFactory {
  public <T> Handler<T> getHandler(Class<T> clazz) {
    if (clazz == String.class) return new StringHandler();
    if (clazz == Date.class) return new DateHandler();
  }
}

I get following error in this factory :

Type mismatch: cannot convert from StringHandler to Handler<T>

How to fix this?

Answer

H&#233;ctor picture Héctor · Dec 15, 2015

SIMPLE SOLUTION

You could save your mappings Class<T> -> Handler<T> in a Map. Something like:

Map<Class<T>, Handler<T>> registry = new HashMap<>();

public void registerHandler(Class<T> dataType, Class<? extends Handler> handlerType) {
    registry.put(dataType, handlerType);
}

public <T> Handler<T> getHandler(Class<T> clazz) {
  return registry.get(clazz).newInstance();
}

In some place, initialize handlers (could be in the factory itself):

factory.registerHandler(String.class, StringHandler.class);
factory.registerHandler(Date.class, DateHandler.class);

And in another place, you create and use them:

Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);

MORE COMPLEX SOLUTION

You can "scan" classes using reflection and, instead of register manually the mappings Class<T> -> Handler<T>, do it using reflection.

for (Class<? extends Handler> handlerType : getHandlerClasses()) {
    Type[] implementedInterfaces = handlerType.getGenericInterfaces();
    ParameterizedType eventHandlerInterface = (ParameterizedType) implementedInterfaces[0];
    Type[] types = eventHandlerInterface.getActualTypeArguments();
    Class dataType = (Class) types[0]; // <--String or Date, in your case
    factory.registerHandler(dataType, handlerType);
}

Then, you create and use them like above:

Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);

To implement getHandlerClasses(), look at this to scan all classes in your jar. For each class, you have to check if it is a Handler:

if (Handler.class.isAssignableFrom(scanningClazz) //implements Handler
    && scanningClazz.getName() != Handler.class.getName()) //it is not Handler.class itself
{
        //is a handler!
}

Hope it helps!