Reflection and Singletons
July 29, 2009 — bigendian
*** Posted on http://bigendian.wordpress.com All rights reserved ***
Java reflection is one of those tools that you may go for years without using, and never miss it. Until the day comes that you have to invoke methods without knowing what they are in advance. When that happens, reflection will be the thing that would prevent you from jumping off the nearest cliff. IF you do it right.
Invoking a method by reflection is fairly straight forward: You get a Class object that describes the class of the method that you are trying to use, get the method you want, and create a method object that represents that method. I wont go into the details or code for this, but you can find a fairly comprehensive guide here.
The tricky part starts when you attempt to invoke the method. The signature for invoking a method requires an object that the method is invoked on, and the list of parameters. In other words, if you are trying to invoke method myMethod() from Class MyClass, the reflection API wants to know which object of MyClass do you want to call that method. The API even goes a step further and lets you specify that a new object should be created for this by invoking the Class.newInstance() method which calls the default no-args constructor of the class. So far so good.
But what happens if the class you are trying to call is a singleton? (I know I said that Java doesn’t have real singletons, but what it does have behaves enough like a singleton to cause this problem) Remember that in a singleton, there are no public constructors, and the only way to get an object of the class is by calling getInstance(). Unfortunately, if you are using reflection you can run into a situation where the object you are invoking at any particular iteration may or may not be a singleton and you wont know in advance, and calling newInstance() on a singleton will definitely not work as advertised. So what do you do?
What you can try to do is something like this:
Object obj = null; Class cls = Class.forName(className); Method methodIWantToInvoke = cls.getMethod(methodName, Params); try{ Method getinst = cls.getMethod("getInstance",null); obj = getinst.invoke(null, null); }catch(NoSuchMethodException n){ try { obj = cls.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } } Object result = methodIWantToInvoke.invoke(obj, params);
What you are doing here is using reflection to get an object so that you can use reflection to invoke a method. 

If the Class object cls describes a class that is a singleton, then the request to getMethod(getInstance(), null) will give ou back a Method object that you can invoke to get the instance of the singleton you need. If it isn’t a singleton, it will throw a MethodNotFoundException, in which case you know you can use plain old newInstance you invoke the constructor. Either way, but the end of this, you can an Object that you can then pass to the method you originally wanted to invoke in order to invoke it.
Note that the order of actions here is arbitrary: You can just as easily call newInstance first and then (upon getting an IllegalAccessException) try getInstance(). You can also combine this with a static factory method to allow calling of constructors other than the no-args, which adds to your flexibility.
Using reflection is realy not as complicated as it might seem, but there are (at least) two things you must by mindful of:
1. Reflection is slow and heavy. It adds a lot of overhead to your code and should be used very judiciously (for a bit of insight to the overhead, checkout Dennis Sosnoski’s Java programming dynamics article. It’s a little dated, and reflection HAS become more efficient, but it’s still no where near directly coding)
2. One of the more common uses of reflection is with serialization, to examine and use de-serialized objects. This is exactly where Java Singletons fails. If you have an Object that was serialized and then deserialized in different JVMs, even though you can successfully call its getInstance() method with the code above, you are still not getting the same object.
Good luck, and happy coding,
B.E.
No comments:
Post a Comment