javasingletongeneric-programming

Singleton Factory - Implementation using Java 8


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

Solution

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