【问题标题】:Factory pattern with generics in javajava中带有泛型的工厂模式
【发布时间】:2017-11-24 00:05:15
【问题描述】:

我在为特定用例使用泛型实现工厂时遇到问题

我有模型类:

class BaseModel { }

class ModelA extends BaseModel { }

class ModelB extends BaseModel { }

及相应的服务:

class Service<T extends BaseModel> { }

class ServiceA extends Service<ModelA> {}

class ServiceB extends Service<ModelB> {}

我的工厂类,根据它服务的模型创建服务:

class Factory {
    private Map<Class<? extends BaseModel>, Class<? extends Service>> registry;

    Factory(){
        registry = new HashMap<>();
        registry.put(ModelA.class, ServiceA.class);
        registry.put(ModelB.class, ServiceB.class);
    }

    Service getService(Class<? extends BaseModel> clazz) throws IllegalAccessException, InstantiationException {
        return registry.get(clazz).newInstance();
    }
}

还有一个使用工厂的类

class Handler<T extends BaseModel>{
    private Service<T> myService;

    Handler(Class<T> modelClass) {
        Factory fact = new Factory();
        try {
            myService = fact.getService(modelClass);
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

我收到一条使用工厂获取服务的线路的警告:

"Unchecked assignment 'Service' to 'Service<T>'"

我明白为什么我会收到消息,因为 getService 方法返回 Service,但我需要 Service&lt;T&gt; 并且对如何更改代码以获取 Service&lt;T&gt; 感到困惑

有什么建议吗?

谢谢!

【问题讨论】:

  • 那么方法getService()有什么返回类型?是Service 还是Service&lt;T&gt;
  • 您可以使用&lt;T extends BaseModel&gt; Service getService(Class&lt;T&gt; clazz) 并将您的返回值转换为Service&lt;T&gt;。您仍然会在方法本身中收到未经检查的警告,但这是 Java 泛型运行时擦除的弱点之一。
  • 有两个问题:第一,没有办法制作一个不同key对应不同泛型类型的Map。根本做不到。其次,没有办法安全地使用反射来创建泛型类型的对象(如ArrayList&lt;String&gt;Service&lt;T&gt;)。您最好从服务中消除通用类型。您能否展示一些使用模型子类型的 Service 方法的示例?
  • @alfasin 我希望它成为 Service 但我不知道如何实现它。
  • @bcsb1001 如果我需要转换,那么我可以全部使用转换而不是?如果我留下一个警告,那么我可以保留这个实现,你不觉得吗?

标签: java generics design-patterns abstract-factory


【解决方案1】:

在这里,您可以将类捕获为 T 并返回它的实例(使用强制转换):

<T extends BaseModel> Service<T> getService(Class<T> clazz) throws IllegalAccessException, InstantiationException {
    return (Service<T>) registry.get(clazz).newInstance();
}

【讨论】:

  • 感谢您的回答!我在这条铸造线上收到未经检查的案例警告
  • 是的,newInstance() 返回对象,这是一个不安全的强制转换,但如果你只收到 T extends BaseModel 并注册所有案例,你将永远不会失败
【解决方案2】:

Java 泛型和继承有时会反直觉地交互,即Service&lt;ModelA&gt;Service&lt;BaseModel&gt; 无关(就继承而言)。但是,您可以编写getService() 的两个实现并显式调用服务构造函数(而不是通过反射)。您的处理程序将需要您的模型实例,以便 Java 覆盖将为您选择正确的服务构造函数。

我在原始答案中没有想到的:您需要捕获工厂中的所有 BaseModel 实现(覆盖将选择最具体的方法),并且您的 Service 必须针对接口实现以提取字符串在一起。

class BaseModel { }

class ModelA extends BaseModel { }

class ModelB extends BaseModel { }

interface Service { }

class ServiceImplementation<T extends BaseModel> implements Service { }

class ServiceFactory {
    public static Service getService(ModelA model) { return new ServiceImplementation<ModelA>(); }
    public static Service getService(ModelB model) { return new ServiceImplementation<ModelB>(); }
    public static Service getService(BaseModel model) { 
        throw new UnsupportedOperationException("Unknown Service Model");
    }
}

class Handler<T extends BaseModel> {
    private Service service;

    Handler(T model) {
        service = ServiceFactory.getService(model);
    }
}

【讨论】:

  • 我不确定如何在保持处理程序通用的同时做到这一点。如果您可以扩展您的回复,那将非常有帮助:)
  • 我刚刚编辑了我的答案,尽量保持简单。 HTH
  • 非常感谢!这给了我一个错误“无法解析方法'getService(T)'”你认为我缺少什么?
  • 不,我应该测试代码,检查我编辑的答案是否缺少什么
猜你喜欢
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多