如何用Java中的泛型实现工厂模式?

use*_*820 11 java generics factory

我有一个通用接口处理程序

public interface Handler<T> {
  void handle(T obj);
}
Run Code Online (Sandbox Code Playgroud)

我可以有这个接口的n个实现.假设我现在有以下2个实现.一个处理String对象,另一个处理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);
  }
}
Run Code Online (Sandbox Code Playgroud)

我想编写一个工厂,它将根据类类型返回处理程序实例.像这样的东西:

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

我在这个工厂得到以下错误:

类型不匹配:无法转换StringHandlerHandler<T>

如何解决这个问题?

Héc*_*tor 7

简单的解决方案

你可以节省您的映射Class<T> -> Handler<T>Map.就像是:

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();
}
Run Code Online (Sandbox Code Playgroud)

在某些地方,初始化处理程序(可能在工厂本身):

factory.registerHandler(String.class, StringHandler.class);
factory.registerHandler(Date.class, DateHandler.class);
Run Code Online (Sandbox Code Playgroud)

在另一个地方,您创建并使用它们:

Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
Run Code Online (Sandbox Code Playgroud)

更复杂的解决方案

您可以使用反射"扫描"类,而不是手动注册映射Class<T> -> Handler<T>,使用反射进行操作.

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);
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以像上面一样创建和使用它们:

Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
Run Code Online (Sandbox Code Playgroud)

要实现getHandlerClasses(),请查看此内容以扫描您的所有类jar.对于每个班级,您必须检查它是否是Handler:

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

希望能帮助到你!

  • 除非我遗漏了一些东西,否则简单的解决方案将无法编译。工厂不能是通用的,因为当您实例化它时,您首先无法提供类型。因此,以下行不会编译:`Map&lt;Class&lt;T&gt;, Handler&lt;T&gt;&gt;registry = new HashMap&lt;&gt;();` 您不能在非通用上下文中使用通用占位符 (2认同)

Old*_*eon 6

您的问题是编译器无法跳转到结果类型正确的事实。

为了帮助编译器,您可以让工厂委托构造。尽管这看起来很奇怪且笨拙,但它确实能够正确维护类型安全,而无需做出诸如强制转换或使用?或原始类型之类的牺牲。

public interface Handler<T> {

    void handle(T obj);
}

public static class StringHandler implements Handler<String> {

    @Override
    public void handle(String str) {
        System.out.println(str);
    }
}

public static class DateHandler implements Handler<Date> {

    @Override
    public void handle(Date date) {
        System.out.println(date);
    }
}

static class HandlerFactory {

    enum ValidHandler {

        String {
                    @Override
                    Handler<String> make() {
                        return new StringHandler();
                    }
                },
        Date {
                    @Override
                    Handler<Date> make() {
                        return new DateHandler();
                    }
                };

        abstract <T> Handler<T> make();
    }

    public <T> Handler<T> getHandler(Class<T> clazz) {
        if (clazz == String.class) {
            return ValidHandler.String.make();
        }
        if (clazz == Date.class) {
            return ValidHandler.Date.make();
        }
        return null;
    }
}

public void test() {
    HandlerFactory factory = new HandlerFactory();
    Handler<String> stringHandler = factory.getHandler(String.class);
    Handler<Date> dateHandler = factory.getHandler(Date.class);
}
Run Code Online (Sandbox Code Playgroud)

  • 我对这个地方进行了未经检查的重写 `Handler&lt;String&gt; make() { return new StringHandler(); }` 因为需要 `&lt;T&gt; Handler&lt;T&gt;` 但得到 `Handler&lt;String&gt;` (2认同)