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

时间:2015-12-15 14:26:52

标签: java generics factory

我有一个通用界面处理程序

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

我可以有这个界面的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);
  }
}

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

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

我在这家工厂遇到以下错误:

  

类型不匹配:无法从StringHandler转换为Handler<T>

如何解决这个问题?

7 个答案:

答案 0 :(得分: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();
}

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

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

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

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

更复杂的解决方案

你可以扫描&#34;使用反射的类,而不是手动注册映射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);
}

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

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

要实施getHandlerClasses(),请查看this以扫描jar中的所有课程。对于每个班级,您必须检查它是否为Handler

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

希望它有所帮助!

答案 1 :(得分:2)

你的问题是编译器无法实现结果类型 正确的事实。

为了帮助编译器,您可以让工厂委托构造。虽然这看起来很奇怪而且笨拙,但它确实能够在不牺牲诸如投射或使用?或原始类型的情况下正确维护类型安全。

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);
}

答案 2 :(得分:0)

您可以使用以下内容:

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

    return null;
  }
}

T是通用的,编译器无法在编译时映射它。使用.equals代替==也更安全。

答案 3 :(得分:0)

使用泛型类型的重点是共享实现。如果您的Handler界面的n实现是如此不同以至于无法共享,那么我认为没有任何理由首先使用定义通用接口。您只需要将StringHandler和DateHandler作为顶级类。

另一方面,如果可以共享实现,就像您的示例一样,那么工厂自然会工作:

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

    static public class PrintHandler<T> implements Handler<T> {
      @Override
      public void handle(T obj) {
        System.out.println(obj);
      }
    }

    static class HandlerFactory {
      public static <T> Handler<T> getHandler() {
        return new PrintHandler<T>();
      }
    }

    public static void main(String[] args) {
      Handler<String> stringHandler = HandlerFactory.getHandler();
      Handler<Date> dateHandler = HandlerFactory.getHandler();

      stringHandler.handle("TEST");
      dateHandler.handle(new Date());
  }
}

答案 4 :(得分:-1)

基本上你可以这样做:

 public Handler getHandler( Class clazz ){
        if( clazz == String.class ) return new StringHandler();
        if( clazz == Date.class ) return new DateHandler();

        return null;
    }

    public static void main( String[] args ){
        HandlerFactory handlerFactory = new HandlerFactory();
        StringHandler handler = ( StringHandler )handlerFactory.getHandler( String.class );
        handler.handle( "TEST" );
        DateHandler handler2 = ( DateHandler )handlerFactory.getHandler( Date.class );
        handler2.handle( new Date() );
    }

输出:

TEST
Tue Dec 15 15:31:00 CET 2015

但是,编写两种不同的方法来分别获取处理程序总是一种更好的方法。

答案 5 :(得分:-1)

我编辑了你的代码并允许Eclipse去修复&#34;错误,它提出了这个。

scala> val filePath = getClass.getResource("myImage.png")
filePath: java.net.URI = file:/home/robert/myImage.png

答案 6 :(得分:-1)

Yout HandlerFactory不了解T.使用您的工厂如下 -

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