当前位置: 首页 > 知识库问答 >
问题:

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

应煌
2023-03-14

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

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

我可以有n个这个接口的实现。假设我现在有以下两个实现。一个处理字符串对象,另一个处理日期

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

如何解决这个问题?


共有3个答案

苏高峰
2023-03-14

使用泛型类型的全部目的是共享实现。如果处理程序接口的n实现差异太大,无法共享,那么我认为没有任何理由使用define这个通用接口。您更希望将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());
  }
}
端木令
2023-03-14

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

为了帮助编译器,您可以让工厂委托构造。尽管这看起来很奇怪,也很笨拙,但它确实能够在不牺牲(比如施法或使用)的情况下正确地维护类型安全性 或原始类型。

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

俞衡虑
2023-03-14

简单解决方案

您可以保存映射

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

更复杂的解决方案

您可以使用反射“扫描”类,而不是手动注册映射

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(),请查看以下内容以扫描jar中的所有类。对于每个类,您必须检查它是否是处理程序

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

希望有帮助!

 类似资料:
  • 问题内容: 我希望返回具有适当泛型的用户界面实例。我陷入下面的错误示例中: 绑定不匹配:BallUserInterfaceFactory类型的通用方法getBaseballUserInterface(BASEBALL)不适用于参数(BALL)。推断的类型BALL不能有效替代有界参数 我了解它不能保证BALL是棒球,因此getBaseballUserInterface方法调用上存在参数类型不匹配的情

  • 我需要初始化类的泛型类型,但无法初始化。就我所做的研究而言,工厂设计模式更适合进行这种泛型类型初始化,但我不知道如何进行。 这是我的通用服务; 当您注意到我的给出了一个错误,为了实现这一点,我需要找到一种初始化Entity的方法。我该如何实现这一点?

  • 我使用方法遇到了麻烦,无论我用什么方法尝试,我都得到了: null 我的代码:

  • 问题内容: 我有一个像这样的通用接口: 此接口具有有限的实例,因此最好将它们实现为枚举值。问题是那些实例具有不同类型的值,因此我尝试了以下方法,但无法编译: 有什么想法吗? 问题答案: 你不能 Java不允许在枚举常量上使用泛型类型。但是,它们允许用于枚举类型: 在这种情况下,你可以做的是为每个泛型类型都拥有一个枚举类型,或者通过将其设为一个类来“伪造”一个枚举: 不幸的是,它们都有缺点。

  • 我正在制作一个餐厅应用程序,我需要使用泛型类型参数。工作流程如下:客户机(观察者)订购食物(界面),FoodFactory返回具体类型(如热狗、薯片等),然后客户机(观察者)收到通知。我需要在这个练习中使用一些课程。在本练习中,类型参数

  • 问题内容: 我正在尝试编写工厂模式以在程序中创建MainMode或TestMode。我以前用来创建这些对象的代码是: 我的游戏(游戏)将根据布尔值(isMode)创建MainMode对象或TestMode对象。如您所见,我正在向我的TestMode对象(randNo())添加一个额外的值。在TestMode中使用此值,以允许用户输入自己的“随机数”,而在MainMode构造函数中,则是随机生成的。