TECHNOLOGY
- Dokuwiki
- Miscellaneous
I have a codebase that uses the the Proxy Pattern to allow dynamic lookups of delegate classnames at runtime. Typically, I use a utility method to retrieve the delegate class name from my database using a key, and then use the Class.forName
method to retrieve the class object. Here's a very simple example, without the utility function:
class ForNameTest { public static void main(String[] args) { try{ String classname = getValueFromDB(...); Object obj = Class.forName(classname); Class<MyClass> myclass = (Class<MyClass> ) obj; MyClass myobject = myclass.newInstance(); ....(code that uses myobject here).... }catch ... } }
The problem with this approach is: the forName()
method returns an object of type Object
, so I'm required to downcast the Object reference to the datatype I expect it to be. Unfortunately, the compiler can't know if that value I pass at runtime is valid, so it produces an unchecked cast
warning at compile time:
ForNameTest.java:6: warning: [unchecked] unchecked cast found : java.lang.Object required: java.lang.Class<MyClass> Class<MyClass> myclass = (Class<MyClass> ) obj;
The warning makes sense. The compiler is basically throwing up its hands and saying “this might not work…its your problem now”. And I agree it should be my problem.
The normal way of resolving the warning is to add the @SuppressWarnings(“unchecked”)
annotation to the method name. It eliminates the warning, but it doesn't really solve the problem. Bad user data will still result in a runtime exception. And that's unacceptable in an app that runs 24×7.
Recently I discovered the Class.asSubclass()
method. It allows me to downcast an object in code, and it throws a ClassCastException
if the cast is improper…which is something I can code for. Here's an example:
class ForNameTest { public static void main(String[] args) { try{ String classname = getValueFromDB("LengthCalcDelegate"); //my utility function Class<?> c = Class.forName(classname); Class<? extends LengthInterface> ntrface = c.asSubclass(LengthInterface.class); LengthInterface length = ntrface.newInstance(); ...(code that uses length here).... }catch(ClassNotFoundException cnfe){ //deal with cnfe }catch(InstantiationException ie){ //deal with ie }catch(IllegalAccessException iae){ //deal with iae } } }
Lots of subtleties here. First, the references to Class<?>
are generic. They eliminate warnings about the use of raw types. Second, the references to Class<? extends LengthInterface>
allow me to cast objects directly to the LengthInterface
and yet still allow me to invoke the newInstance
method on the class object. And third, all human-related errors at runtime are dealt with explicitly in code.
So now I have type safety, proper exception handling, and no compiler warnings. Perfect!