I have a Trigger Manager scenario where I delegate the triggers (in other-words subscribe triggers) to different handlers.
For now I have three handler types, I use a switch-case with enum (enum here is the handler type) to redirect to correct handler.
But my code seems not extensible, its not generic and it doesn't follow SOLID principle. Imagine if I need to have more handler
I will be eventually coming and editing my switch case code and I will have more cases where it affects the cyclomatic complexity of my code
Below is my exact code snippet
private static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType) throws TriggerHandlerException {
switch (triggerHandlerType) {
case DASHBOARD_HANDLER:
triggerContext = new TriggerContext(new DashboardTriggerHandler());
return triggerContext;
case COMPONENT_HANDLER:
triggerContext = new TriggerContext(new ComponentTriggerHandler());
return triggerContext;
case WIDGET_HANDLER:
triggerContext = new TriggerContext(new WidgetTriggerHandler());
return triggerContext;
default:
LOGGER.error(MIS_CONFIGURED_REQUEST_IS_PROVIDED);
throw new TriggerHandlerException(TRIGGER_HANDLER_TYPE_GIVEN_IS_NOT_CONFIGURED_IN_THE_LIST_OF_TRIGGER_HANDLERS);
}
}
Can someone help me to enhance this code in-which I can make it more generic and avoid cyclomatic complexity and follow SOLID Principle along with some design pattern.
I think you mean "make code more dynamic", and your problem comes from using objects as primitives.
Rather than switching on the enum object, your enum objects should contain the type to be instantiated:
enum TriggerHandlerType {
DASHBOARD {
#Override
TriggerHandler create() {
return new DashboardTriggerHandler();
}
},
COMPONENT_HANDLER {
//...
};
abstract TriggerHandler create();
}
getTriggerContext can then call create() to instantiate the handler:
private static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType) throws TriggerHandlerException {
return new TriggerContext(triggerHandlerType.create());
}
I am not sure about the overall design structure, but the switch can be replaced with a newHandler() method on the enum.
private static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType)
throws TriggerHandlerException
{
return new TriggerContext(triggerHandlerType.newHandler());
}
In the enum you would implement the method for each type enum as
enum TriggerHandlerType {
DASHBOARD_HANDLER
{
Handler newHandler() { return new DashboardHandler(); }
},
...;
abstract Handler newHandler();
}
You could use a map of configurations for that:
// All your triggers classes should implement this interface
interface TriggerHandler {}
// For example:
public static class DashboardTriggerHandler implements TriggerHandler {
}
// Create your configuration
static Map<TriggerHandlerType, Class> contexts;
static {
contexts = new HashMap<>();
contexts.put(TriggerHandlerType.DASHBOARD_HANDLER, DashboardTriggerHandler.class);
contexts.put(TriggerHandlerType.COMPONENT_HANDLER, ComponentTriggerHandler.class);
contexts.put(TriggerHandlerType.WIDGET_HANDLER, WidgetTriggerHandler.class);
}
// Return your instance through reflection
public static TriggerContext getTriggerContext(TriggerHandlerType triggerHandlerType) throws TriggerHandlerException, IllegalAccessException, InstantiationException {
Class className = contexts.get(triggerHandlerType);
if (className == null) {
throw new TriggerHandlerException();
}
return new TriggerContext((TriggerHandler)className.newInstance());
}
Related
I write the code for creating multiple instances for the thread-safe factory creation:
public class XWebserviceObjectFactoryCreator {
}
However, the code looks repeating to me and not happy about it. Is it possible to use enum (or something else) to make it more readable?
Since the example shows all the classes have no-argument constructors, you can use the Class.newInstance method, so something like :
private static Map<Class<?>, Object> instances = new HashMap<>();
public static <T extends Object> T getObjectFactoryInstance(Class<T> clazz) {
Object result;
if ((result = instances.get(clazz)) == null) {
synchronized (instances) {
if ((result = instances.get(clazz)) == null) {
try {
result = clazz.newInstance();
instances.put(clazz, result)
} catch (InstantiationException | IllegalAccessException e) {
// do something
}
}
}
}
return (T)result;
}
Apologies if layout or syntax is off, but I’m away from my computer and doing this on the phone - but hope you get the idea !
If any classes do require arguments in their constructors, then you’ll have to use reflection to invoke the constructor.
Finally, note that I’ve modified the declaration of clazz from Class<?> to Class< T>, to let the generics validate the caller by tying the output to the given Class.
you can use the Abstract Factory design Pattern along with Enum where Sales2ObjectFactory, OrderingObjectFactory, Settings2ObjectFactory, and SettingsObjectFactory can be a factory in itself with a common interface.
Then you can use Enum to get the instance of one of those factories.
public static class FactoryMaker {
public enum FactoryType {
SALES2OBJECT, ORDERING,SETTING2OBJECT,SETTINGS
}
public static CommonFactory makeFactory(FactoryType type) {
switch (type) {
case SALES2OBJECT:
return new Sales2ObjectFactory();
case ORDERING:
return new OrderingObjectFactory();
case SETTING2OBJECT:
return new Settings2ObjectFactory();
case SETTINGS:
return new new SettingsObjectFactory();
default:
throw new IllegalArgumentException("FactoryType not supported.");
}
}
}
Please check :: https://java-design-patterns.com/patterns/abstract-factory/
I am wondering if i can replace the generic type upon class initialization ( without inheritance ).
The case is as below .
I have bunch of generic actor + builder class in my module and distribute this to people who wants to use it in my team while keeping the actor and the entry point of fluent interface generic.
The requirement is how can people supply their own builder ( not extending ) to do the stuff they want.
Current state:
class MessageBuilder {
public MessageBuilder msg(String msg) {
//do something
}
}
class Actor {
public MessageBuilder newMessage() {
return new MessageBuilder();
}
}
class Main {
#Test
public void testActor() {
Actor actor = new Actor();
actor.newMessage().msg("sss").send();
}
}
Desired state:
class MessageBuilder{
public MessageBuilder msg(String msg) {
//do something
}
//more fluent api...
}
// project specific - dont want to extend from generic one as this should be contains its own fluent interface
class MyCustomMessageBuilder {
public MyCustomMessageBuilder rawstr(String rawstr) {
//do something
}
}
class Actor<T> {
public T newMessage() {
return (T)builderFactory.getInstance();
}
}
class Main {
#Test
public void testActor() {
Actor<MyCustomMessageBuilder> actor = new Actor(BuilderFactory);
actor.newMessage().rawstr("sss").send();
}
}
It's not possible without some known tricks.
First, Java implements Generics with type erasure (more information on type erasure), therefore the compiler will:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
In practice, this means that Actor<Builder1> and Actor<Builder2> are the exact same class after it gets compiled. In both cases, newMessage is implemented as follows:
class Actor {
public Object newMessage() {
...
}
}
It's not possible for newMessage to have different implementations based on the type parameter and it's not possible for newMessage to ask for the type of T because it gets removed.
Having said that, you can pass in type information:
class Actor<T> {
private Class<T> klass;
public Actor(Class<T> klass) {
this.klass = klass;
}
public T newMessage() {
return klass.newInstance();
}
}
class Main {
#Test
public void testActor() {
Actor<MyCustomMessageBuilder> actor = new Actor<>(MyCustomMessageBuilder.class);
actor.newMessage().rawstr("sss").send();
}
}
I'd go with a factory approach. The builder should be supplied by a factory:
class Actor<MsgBuilder> {
private final Supplier<MsgBuilder> messageBuilderFactory;
public Actor(Supplier<MsgBuilder> builderFactory) {
this.messageBuilderFactory = builderFactory;
}
public MsgBuilder newMessage() {
return messageBuilderFactory.get();
}
}
This way offers flexibility in creating the message builder without sacrificing type safety and also no need for ugly casting.
As SOLID principles say, it's better to remove switch conditions by converting them to classes and interfaces.
I want to do it with this code:
Note: This code is not real code and I just put my idea into it.
MessageModel message = getMessageFromAnAPI();
manageMessage(message);
...
void manageMessage(MessageModel message){
switch(message.typeId) {
case 1: justSave(message); break;
case 2: notifyAll(message); break;
case 3: notify(message); break;
}
}
Now I want to remove switch statement. So I create some classes for it and I try to implement a polymorphism here:
interface Message{
void manageMessage(MessageModel message);
}
class StorableMessage implements Message{
#Override
public void manageMessage(MessageModel message) {
justSave(message);
}
}
class PublicMessage implements Message{
#Override
public void manageMessage(MessageModel message) {
notifyAll(message);
}
}
class PrivateMessage implements Message{
#Override
public void manageMessage(MessageModel message) {
notify(message);
}
}
and then I call my API to get my MessageModel:
MessageModel message = getMessageFromAnAPI();
Now my problem is here. I have my model and I want manage it using my classes. As SOLID examples, I should do something like this:
PublicMessage message = new Message();
message.manageMessage(message);
But how can I know which type is related to this message to make an instance from it(PublicMessage or StorableMessage or PrivateMessage)?! Should I put switch block here again to do it or what?
You can do this:
static final Map<Integer,Consumer<MessageModel>> handlers = new HashMap<>();
static {
handlers.put(1, m -> justSave(m));
handlers.put(2, m -> notifyAll(m));
handlers.put(3, m -> notify(m));
}
This will remove your switch to
Consumer<Message> consumer = handlers.get(message.typeId);
if (consumer != null) { consumer.accept(message); }
Integration Operation Segregation Principle
You should of course encapsulate this:
class MessageHandlingService implements Consumer<MessageModel> {
static final Map<Integer,Consumer<MessageModel>> handlers = new HashMap<>();
static {
handlers.put(1, m -> justSave(m));
handlers.put(2, m -> notifyAll(m));
handlers.put(3, m -> notify(m));
}
public void accept(MessageModel message) {
Consumer<Message> consumer = handlers.getOrDefault(message.typeId,
m -> throw new MessageNotSupportedException());
consumer.accept(message);
}
}
with your client code
message = getMessageFromApi();
messageHandlingService.accept(message);
This service is the "integration" part (as opposed to the "implementation": cfg Integration Operation Segregation Principle).
With a CDI framework
For a production environment with a CDI framework, this would look something like this:
interface MessageHandler extends Consumer<MessageModel> {}
#Component
class MessageHandlingService implements MessageHandler {
Map<Integer,MessageHandler> handlers = new ConcurrentHashMap<>();
#Autowired
private SavingService saveService;
#Autowired
private NotificationService notificationService;
#PostConstruct
public void init() {
handlers.put(1, saveService::save);
handlers.put(2, notificationService::notifyAll);
handlers.put(3, notificationService::notify);
}
public void accept(MessageModel m) { // as above }
}
Behavior can be changed at Runtime
One of the advantages of this vs the switch in #user7's answer is that the behavior can be adjusted at runtime. You can imagine methods like
public MessageHandler setMessageHandler(Integer id, MessageHandler newHandler);
which would install the given MessageHandler and return the old one; this would allow you to add Decorators, for example.
An example for this being useful is if you have an unreliable web service supplying the handling; if it is accessible, it can be installed as a handlelr; otherwise, a default handler is used.
You can use a factory in this case to get the instance of Message. The factory would have all instances of Message and returns the appropriate one based on the MessageModel's typeId.
class MessageFactory {
private StorableMessage storableMessage;
private PrivateMessage privateMessage;
private PublicMessage publicMessage;
//You can either create the above using new operator or inject it using some Dependency injection framework.
public getMessage(MessageModel message) {
switch(message.typeId) {
case 1: return storableMessage;
case 2: return publicMessage;
case 3: return privateMessage
default: //Handle appropriately
}
}
}
The calling code would look like
MessageFactory messageFactory; //Injected
...
MessageModel messageModel = getMessageFromAnAPI();
Message message = messageFactory.getMessage(messageModel);
message.manageMessage(messageModel);
As you can see, this did not get rid of the switch entirely (and you need not as using switch is not bad in itself). What SOLID tries to say is to keep your code clean by following SRP (Single Responsibility Principle) and OCP (Open-Closed Principle) here. What it means here is that you code shouldn't have the actual processing logic to handle for each typeId in one place.
With the factory, you have moved the creation logic to a separate place and you have already moved the actual processing logic to respective classes.
EDIT:
Just to reiterate - My answer focuses on the SOLID aspect of the OP. By having separate handler classes (an instance of Message from the OP) you achieve the SRP. If one of the handler classes changes, or when you add a new message typeId (message.typeId) (i.e, add a new Message implementation) you need not modify the original and hence you achieve OCP. (On assumption that each of these does not contain trivial code). These are already done in the OP.
The real point of my answer here is to use a Factory to get a Message. The idea is to keep the main application code clean and limit the usages of switches, if/else and new operators to instantiation code. (Similar to #Configuration classes/ the classes that instantiate Beans when using Spring or Abstract modules in Guice). The OO principles do not say using switches are bad. It depends on where you use it. Using it in the application code does violate the SOLID principles and that is what I wanted to bring out.
I also like the idea from daniu# to use a functional way and the same can even be used in the above factory code (or can even use a simple Map to get rid of the switch).
The main point here is that you separate instantiation and configuration from execution.
Even with OOP we cannot avoid to distinguish between different cases using if/else cascades or switch statements. After all we have to create instances of specialized concrete classes.
But this should be in initialization code or some kind of factory.
Within the business logic we want to avoid if/else cascades or switch statements by calling generic methods on interfaces where the implementer know better themselves how to behave.
The usual clean code approach is for the MessageModel to contain its behavior.
interface Message {
void manage();
}
abstract class MessageModel implements Message {
}
public class StoringMessage extends MessageModel {
public void manage() {
store();
}
}
public class NotifyingMessage extends MessageModel {
public void manage() {
notify();
}
}
Your getMessageFromApi then returns the proper type, and your switch is
MessageModel model = getMessageFromApi();
model.manage();
This way, you essentially have the switch in the getMessageFromApi() method because it has to decide which message to generate.
However, that is fine because it does fill the message type id anyway; and the client code (where your switch currently resides) is resistent to changes to the messages; ie adding another message type will be be handled correctly.
The real problem you have is that MessageModel isn't polymorphic. You need to convert the MessageModels to a polymorphic Message class, but you shouldn't put any of the logic of what to do with the messages in this class. Instead, it should contain the actual contents of the message, and use the visitor pattern, as shown in Eric's Answer, so that other classes can operate on a Message. You don't need to use an anonymous Visitor; you can create implementing classes like a MessageActionVisitor.
To convert MessageModels to various Messages, you can use a factory, as shown in user7's answer. In addition to selecting which type of Message to return, the factory should fill in the fields of each type of Message using the MessageModel.
You can use the Factory Pattern. I would add an enum which has the values:
public enum MessageFacotry{
STORING(StoringMessage.TYPE, StoringMessage.class),
PUBLIC_MESSAGE(PublicMessage.TYPE, PublicMessage.class),
PRIVATE_MESSAGE(PrivateMessage.TYPE, PrivateMessage.class);
Class<? extends Message> clazz;
int type;
private MessageFactory(int type, Class<? extends Message> clazz){
this.clazz = clazz;
this.type = type;
}
public static Message getMessageByType(int type){
for(MessageFactory mf : values()){
if(mf.type == type){
return mf.clazz.newInstance();
}
}
throw new ..
}
}
Then you can call the static method of that enum and create an instance of the Message you want to manage.
You can use the Factory pattern and Visitor pattern together.
you can create a factory like this:
class MessageFactory {
public Message getMessage(MessageModel message) {
switch(message.typeId) {
case 1: return new StorableMessage((MessageModelType1) message);
case 2: return new PrivateMessage((MessageModelType2) message);
case 3: return new PublicMessage((MessageModelType3) message);
default: throw new IllegalArgumentException("unhandled message type");
}
}
}
and declare your messages like this:
interface Message {
void accept(Visitor visitor);
}
class StorableMessage implements Message {
private final MessageType1 message;
public StorableMessage(MessageModelType1 message) {
this.message = message;
}
#Override
public <Result> Result accept(Visitor<Result> visitor) {
return visitor.visit(this);
}
public MessageModelType1 getMessage() {
return message;
}
}
class PublicMessage implements Message {
...
}
class PrivateMessage implements Message {
...
}
and declare a Visitor like this:
interface Visitor {
void visit(StorableMessage message);
void visit(PublicMessage message);
void visit(PrivateMessage message);
}
and replace your switch statements with this:
Message message = ....;
message.accept(new Visitor() {
#Override
public void visit(StorableMessage message) {
justSave(message.getMessage());
}
#Override
public void visit(PublicMessage message) {
notifyAll(message.getMessage());
}
#Override
public void visit(PrivateMessage message) {
notify(message.getMessage());
}
});
If you want, instead of writing an anonymous class, you can create a class MessageModelFactory that has a private Visitor, and use that instead. in that case, it might be better to make the Visitor interface like this:
interface Visitor<Result> {
Result visit(StorableMessage message);
Result visit(PublicMessage message);
Result visit(PrivateMessage message);
}
I have a hierarchy of classes RequestParams , all can be created with ExtArgs.
public interface RequestParams {
}
I also created a Factory RequestsFactory looking like this:
public class RequestFactory<P extends RequestParams> {
private final Logger logger = LoggerFactory.getLogger(RequestFactory.class);
final Class<P> paramsClass;
public RequestFactory(Class<P> paramsClass) {
this.paramsClass = paramsClass;
}
public Request<P> create(ExternalArgs args) {
P params = null;
try {
params = paramsClass.getDeclaredConstructor(ExternalArgs.getClass()).newInstance(args);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
logger.error("Failed to generate request", e);
}
return new Request<>("get", Collections.singletonList(params));
}
static <P extends RequestParams> RequestFactory<P> createFactory(
final Class<P> paramsClass) {
return new RequestFactory<P>(paramsClass);
}
}
where my client code is s follows:
Request devInfoRequest = RequestFactory.createFactory(RequestType.SYSTEM_INFO.getRequestClass()).create(externalArgs);
I use this ENUM in order map request types:
public enum RequestType {
SYSTEM_INFO(DeviceInfoParams.class),
INTERFACES(InterfacesParams.class)
}
My question is - How can use this, since I can't add neither constructor nor static builder to the RequestParams interface (or abstract class) ?
EDIT: (After AdamSkywalker's answer):
I want my client code to be:
Request<InterfacesParams> interfacesRequest =
RequestFactory.<InterfacesParams>createFactory(RequestType.INTERFACES.create(externalArgs)).create("get");
But it requires the enum return function to match the generic type.
As you've observed, the problem with the built-in enum feature is that every enum-value has to have the same type; they can't have different type arguments (in fact, the enum-class won't even accept a type parameter).
One option is to roll your own enumeration — just write a class RequestType<P extends RequestParam> with a private constructor and a normal static field for each instance.
But if the RequestType and RequestFactory<...> types really only have what you've shown here, then it probably makes more sense to just merge them, by writing:
public final class RequestFactory<P extends RequestParams> {
public static final RequestFactory<DeviceInfoParams> SYSTEM_INFO =
new RequestFactory<>(DeviceInfoParams::new);
public static final RequestFactory<InterfacesParams> INTERFACES =
new RequestFactory<>(InterfacesParams::new);
private final Function<ExternalArgs, P> mRequestParamsCreator;
private RequestFactory(final Function<ExternalArgs, P> requestParamsCreator) {
mRequestParamsCreator = requestParamsCreator;
}
public Request<P> create(final ExternalArgs externalArgs) {
final P requestParams = mRequestParamsCreator.apply(externalArgs);
return new Request<P>("get", Collections.singletonList(requestParams));
}
}
(Note: the above uses some Java 8 / JDK 1.8 features for conciseness. If you're still using an older version of Java, the above approach will still work, but you'll have to write a bit of boilerplate to fill in the gaps. Likewise if the relevant constructors of DeviceInfoParams and InterfacesParams declare any checked exceptions, since in that case they can't be implicitly converted to java.util.function.Functions.)
You can then create requests by writing (for example):
final Request<DeviceInfoParams> systemInfoRequest =
RequestFactory.SYSTEM_INFO.create(externalArgs);
final Request<InterfacesParams> interfacesRequest =
RequestFactory.INTERFACES.create(externalArgs);
As with AdamSkywalker's approach, this avoids reflection completely.
Maybe it's not exactly what you're looking for, but your code is so over engineered. Why not:
public enum RequestType {
SYSTEM_INFO {
public RequestParams create(ExternalArgs args) { return new DeviceInfoParams(args); }
},
INTERFACES {
public RequestParams create(ExternalArgs args) { return new InterfacesParams(args); }
};
abstract RequestParams create(ExternalArgs args);
}
And then call it without reflection tricks:
Request req = new Request("get", singletonList(SYSTEM_INFO.create(args)));
You may still use your factory, but the point is to remove the reflection complexity.
Hope you can help me with this:
I have ...
a string list of class names called classNameList
a generic class Geography<T>
a static generic method <T> void read(Class<T> cl, Geography<T> geo)
I want to loop through the string class name list and call the generic method for each of these classes.
What I tried but obviously did not work:
for (int i = 0; i < classNameList.length; i++) {
Class<?> myClass = Class.forName(classNameList[i].getName());
Geography<myClass.newInstance()> geo;
read(myClass, geo);
}
Error: myClass.newInstance cannot be resolved to a type
My code runs perfectly for a single call of the generic function:
Geography<ExampleClass> ExampleGeo;
read(ExampleClass.class, ExampleGeo);
Any ideas how I could do this?
UPDATE:
Thanks for the helpful input, still it's hard for me to adopt it to my real code.
So this is the non simplyfied problem:
I do ready in shapefile-Data with a shapefileLoader, for each feature of the Shapefile a class (GuadAgent) is initialized with a predifined class (PlantWind). I have shapefiles in my input-directory with the names of the Classes their features do represent. I want Java to read in the shapefiles and create the respective agent class. (the agents are also placed in a context and a geography..)
Used classes are: ShapefileLoader, Geography, the other classes can be find at the same website
This part is in the main-method:
Geography<GuadAgent> guadGeography = GeographyFactoryFinder.createGeographyFactory(null).createGeography("guadGeography", context, new GeographyParameters<GuadAgent>());
Context<GuadAgent> context = new DefaultContext<GuadAgent>();
FileFilter filter = new FileFilter() {
#Override
public boolean accept(File file) {
return file.getName().endsWith(".shp"); // return .shp files
}
};
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
File folder = new File(shapefileDir);
File[] listOfFiles = folder.listFiles(filter);
for (File classFile : listOfFiles) {
try {
readForName(classFile,context,guadGeography);
} catch (ClassNotFoundException | MalformedURLException
| FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The static Method that reads in the names:
static <T> void readForName(File classFile, Context<GuadAgent> context,Geography<GuadAgent> guadGeography) throws ClassNotFoundException, MalformedURLException, FileNotFoundException {
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
String className = classFile.getName().split("\\.(?=[^\\.]+$)")[0];
File shapefile = null;
shapefile = new File(shapefileDir+classFile.getName());
if (!shapefile.exists()) {
throw new FileNotFoundException("Could not find the given shapefile: " + shapefile.getAbsolutePath());
}
switch (className) {
case "PlantWind":
ShapefileLoader<PlantWind> PlantWindLoader = new ShapefileLoader<PlantWind>(PlantWind.class,shapefile.toURI().toURL() , guadGeography, context);
PlantWindLoader.load();
PlantWindLoader.close();
System.out.println(context.getObjects(PlantWind.class).size());
break;
// Todo Add other Agent types
default:
break;
}
How can I get rid of the switch? Although their number is finit, there are very many different agents...
Unfortunately, there's no syntax close to your intention (nice idea though).
The basic problem is that Class.forName() returns an unknown Class<?>, so you need a cast somewhere. It's just a mater of where you put it.
I suggest this approach (which compiles) that bundles up doing a read() based on a class name:
static <T> void readForName(String className) throws ClassNotFoundException {
Class<T> myClass = (Class<T>) Class.forName(className);
Geography<T> geo = new Geography<T>(); // No code shown. Adjust as required
read(myClass, geo);
}
May I also suggest using the foreach loop syntax, for tidier code:
for (String className : classNameList) {
readForName(className.getName());
}
Creating instances from Generic Types at Runtime
I am not entirely clear on what you are trying to accomplish, but at first look it looks like the simplest solution is the best solution.
It could be solved with using a scripting environment ( Groovy, JavaScript, JRuby, Jython ) that could dynamically evaluate and execute arbitrary code to create the objects, but that got extremely convoluted and overly complex, just to create an object.
But unfortunately I think it has a very pedestrian solution.
As long as there is a predefined set of supported types, you can use a Factory pattern. Here I just leverage the Provider<>T interface from the javax.inject/com.google.inject package.
Q26289147_ProviderPattern.java
public class Q26289147_ProviderPattern
{
private static final List<String> CLASS_NAMES = ImmutableList.of("String", "Integer", "Boolean");
private static final Map<String, Provider<StrawManParameterizedClass>> PROVIDERS;
static
{
final ImmutableMap.Builder<String, Provider<StrawManParameterizedClass>> imb = ImmutableMap.builder();
for (final String cn : CLASS_NAMES)
{
switch (cn)
{
case "String":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<String> get() { return new StrawManParameterizedClass<String>() {}; }
});
break;
case "Integer":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
case "Boolean":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
default:
throw new IllegalArgumentException(String.format("%s is not a supported type %s", cn, Joiner.on(",").join(CLASS_NAMES)));
}
}
PROVIDERS = imb.build();
}
static <T> void read(#Nonnull final StrawManParameterizedClass<T> smpc) { System.out.println(smpc.type.toString()); }
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
#Override
public String toString() { return type.getRawType().getCanonicalName(); }
}
public static void main(final String[] args)
{
for (final String cn : CLASS_NAMES)
{
read(PROVIDERS.get(cn).get());
}
}
}
Disclaimer:
This is just a proof of concept example, I would never use a switch
statement like that in production code I would use a Strategy
Pattern or Chain of Responsibility Pattern to encapsulate the logic
of what type to create based on the ClassName key.
This initially looked like a generics problem, it isn't, it is a creation problem.
That said, you don't need to pass around instances of Class<?> you can get Generic Type information off of Parameterized classes at runtime with TypeToken from Guava.
You can even create instances of any generic type at runtime with TypeToken from the Guava library.
The main problem is this syntax isn't supported: Geography<myClass.newInstance()> geo; and I can't think of anyway to fake it other than the Provider implementation above.
Here is a straw man example of how to use TypeToken so that your
parameterized classes will always know their types!
Q26289147.java
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Notes:
Works great for classes that have a default no arg constructor.
Works better than using straight reflection if there are no default no arg constructors.
Should play well with Guice allowing you to use the ".getRawType()generatedClassto pass togetInstance()` of an Injector. have not tried this yet, I just thought of it!
You can use Class<T>.cast() to do casting that doesn't need #SuppressWarning("unchecked") all over the place.`
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
Update: I'm not sure what the Geography class is meant to do. If it needs a generically typed object, it might look like this:
public class Geography<T> {
private final T data;
public Geography(T data) {
this.data = Objects.requireNonNull(data);
}
}
If it needs a class, the constructor might look like this:
public class Geography<T> {
private final Class<T> dataClass;
public Geography(Class<T> cls) {
this.dataClass = Objects.requireNonNull(cls);
}
}