views:

977

answers:

5

I want to instantiate one instance of a class from the string name of the class. ( using Class.forName().newInstance(). )

Here's the problem: I want that instance to be a singleton.. I could do this using a singleton pattern, except that newInstance calls the default constructor for a class, and with a singleton, that constructor must be "private"..

Is there a solution? I could think of a less elegant way to do this (using a hashmap as a lookup table..), but would prefer a better solution..

Thanks,

A: 

You could use an enum and have a HashMap named for each of them. Or you could use some dependency injection framework to get a singleton of something (Guice?)

Edit Okay, I jump in with a code sample too:

package tests;
public class EnumSingleton {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("tests.Singleton1");
        Operation instance = Operation.class.cast(c.getEnumConstants()[0]);
        System.out.println(instance.getTHEAnswer());

        c = Class.forName("tests.Singleton2");
        instance = Operation.class.cast(c.getEnumConstants()[0]);
        System.out.println(instance.getTHEAnswer());
    }
}
interface Operation {
    int getTHEAnswer();
}
enum Singleton1 implements Operation {
    INSTANCE;
    @Override
    public int getTHEAnswer() {
        return 42;
    }
}
enum Singleton2 implements Operation {
    INSTANCE;
    @Override
    public int getTHEAnswer() {
        return 84;
    }
}

And its safe and sound. Edit And has some meaning now.

kd304
+8  A: 

A classic singleton also has a static getInstance() method - call that via reflection instead of using newInstance(). Yeah, it's more work, but that's how it is...

Or you could use setAccessible() to call the private constructor anyway, but you'd break the singleton and go to hell.

Thirdly, you could avoid having a Singleton altogether and find a better soltion (there usually is).

Michael Borgwardt
s/usually/always/
Tom Hawtin - tackline
+2  A: 

You could use reflection to get a reference to a static factory method of the class, and then invoke that. The factory method could enforce the singleton pattern

Class c = Class.forName("test.MyClass");
Method factoryMethod = c.getDeclaredMethod("getInstance");
Object singleton = factoryMethod.invoke(null, null);

And then

public class MyClass {

   private static MyClass instance;

   private MyClass() { 
      // private c'tor 
   }

   public static synchronized MyClass getInstance() {
      if (instance == null) {
         instance = new MyClass();
      }
      return instance:
   }
}

Warning: Singleton design pattern may be detrimental to your longterm health.

skaffman
Nice, still I would like to know why the OP wants to have a singleton by name?
kd304
Maybe they're re-inventing Spring.
skaffman
Lot's of little singletons!
Tom Hawtin - tackline
A: 

You'll have to know the method name of your singleton class that constructs the object. If it's e.g. called "instance()" you can do something like

Class c = Class.forName("MySingleton");
Method m = c.getDeclaredMethod("instance",null);
MySingleton singleton = m.invoke(null,null);
nos
A: 

Can you instead rely upon the existence of a static factory method? It would rely upon some manner of naming convention, but that would work.

Bottom line: if a class is to be known to be singleton then it must have responsibility for that policing. Hence it must conform to some implementation of the pattern, and if you want a dynamic selection amongst such classes then you need to be able to predict the "Factory" approach.

djna