Handling Compiler Warnings
Unchecked Cast using Class.forName
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 24x7.
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!