I'm having trouble implementing factory with generics for a specific use case
I have model classes:
class BaseModel { }
class ModelA extends BaseModel { }
class ModelB extends BaseModel { }
And corresponding services:
class Service<T extends BaseModel> { }
class ServiceA extends Service<ModelA> {}
class ServiceB extends Service<ModelB> {}
My Factory class, to create service according to the model it services:
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();
}
}
And a class that uses the factory
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();
}
}
}
I get an warning for the line that uses the factory to get the service:
"Unchecked assignment 'Service' to 'Service<T>'"
I understand why I get the message, since the getService
method returns Service
, but I need Service<T>
and puzzled about how I can change the code to get Service<T>
Any suggestions?
Thanks!
Java generics and inheritance sometimes interact counter-intuitively, i.e. Service<ModelA>
is not related (in terms of inheritance) to Service<BaseModel>
. However, you can write two implementations of getService()
and call the service constructors explicitly (not through reflection). Your Handler will need an instance of your Model, such that Java overriding will select the correct service-constructor for you.
What I haven't thought about in my original answer: you need to catch for all BaseModel implementations in your factory (overriding will pick the most specific method) and also your Service has to be implemented against an interface to pull the strings together.
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);
}
}