Typesafe forName class loading
How do I call Class.forName()
when the result is a generic type? Usually I can use asSubclass()
, but here the only way I see to do it is a cast, which kindof sticks out & bugs me when everything else is nicely typed with generics.
The scenario goes something like this:
There is a .jar with one entry point main class that has a main()
. It takes an option of a classname (and some others, irrelevant here). The class given implements Callable<Integer>
. This class is loaded, inited & launched.
Here is an example of what I need:
Class<? extends Callable<Integer>> clazz = (Class<? e开发者_开发知识库xtends Callable<Integer>>) Class.forName(options.valueOf(className)).asSubclass(Callable.class);
Is there any way to get rid of that cast?
Using SE6.
First you probably want a full generic Class
Class<Callable<Integer>> classCI = ...;
Then the java type system has no problem with
Class<? extends Callable<Integer>> clazz =
Class.forName(options.valueOf(className))
.asSubclass(classCI);
How can we get classCI
? We can cheat by unchecked cast
Class<Callable<Integer>> classCI = (Class<Callable<Integer>>)Callable.class;
This is inherently unsafe. There must be external forces to make sure the className
really is a Callable<Integer>
. For example if it's a Callable<String>
, the program runs through all the casts without any problem, and it only blows up much later when Integer call()
is invoked, and the error message will be very misleading.
It's ok if a cast cannot be analyzed statically to succeed:
Object o = ...;
String s1 = (String)o; // may fail, no javac warning
String s2 = String.class.cast(o); // may fail, no javac warning
as long as an exception is immediately thrown when the cast fails at runtime.
To be type safe, we must proactively check the generic type of the className
@SuppressWarning( "unchecked" )
Class<? Callable<Integer>> getClass(String className)
{
Class clazz = Class.forName(className);
via reflection, check generic super interfaces of clazz
if there's no Callable<Integer> super interface
throw "className is not a Callable<Integer>"
// we have *checked*, the following cast is safe
return (Class<? Callable<Integer>>)clazz;
}
We are justified to suppress "unchecked" here, because the implementation checks to make sure that if the className
doesn't really denote a class implementing Callable<Integer>
, it immediately throws an exception right there. Our cast is "checked", and the program is type safe.
精彩评论