I want to implement a generic singleton factory pattern where I pass the Class of the required object as a parameter and the factory class should check in the map if there's already an object created for it, if its, return the object from map. If not, create a new instance, put it in the map and return the instance.
I can have a generic return type as Object, but I don't want to cast the returned object at every place I call the get instance method.
The below is the code: I get a compilation error at the line c.cast(instance);
We do not use spring/dependency injection, but trying implement common class to take care of creating all singleton objects.
public class SingletonFactory {
public static Map<String,Object> objectFactory = new HashMap<String, Object>();
public static <T extends Object> T getInstance(Class<?> c){
String key = c.toString();
Object instance= objectFactory.get(key);
if (instance == null) {
synchronized (c) {
try {
instance = c.newInstance();
objectFactory.put(key, instance);
} catch(IllegalAccessException | InstantiationException e) {
throw new RuntimeException("Exception while creating singleton instance for class : "+key+" - Exception Message : "+e);
}
}
}
return c.cast(instance);
}
}
First, I can point out that <T extends Object>
can be replaced with just <T>
because everything in Java, involving generics, must be an Object.
The second part that you're really close on is Class<?> c
. That says that you can pass any class in and it will return whatever type T
is. c.cast(instance)
can be replaced with (T) instance
if you think that looks better but, there's actually a difference which goes into more detail here: Java Class.cast() vs. cast operator .
The final code looks like this:
public class SingletonFactory {
public static Map<String,Object> objectFactory = new HashMap<String, Object>();
public static <T> T getInstance(Class<T> c){
synchronized (c) {
String key = c.toString();
Object instance= objectFactory.get(key);
if (instance == null) {
try {
instance = c.newInstance();
objectFactory.put(key, instance);
} catch(IllegalAccessException | InstantiationException e){
throw new RuntimeException("Exception while creating singleton instance for class : "+key, e);
}
}
return c.cast(instance);
// or
return (T) instance;
}
}
}
Also if you really wanted to, you could keep everything in your original code and cast instance to T
at the end of the method and it should work. The only thing is your method calls would look like SingletonFactory.getInstance<Foo>(Foo.class)
instead of SingletonFactory.getInstance(Foo.class)
. That is because of the Class<?>
in your original code instead of Class<T>
.
EDIT: I also changed the code to synchronize earlier thanks @Khan9797
SECOND EDIT: Made the instantiation of the RuntimeException pass e
as the cause. (It is best practice in case this exception arises, you want a complete stacktrace)