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<? extends Callable<Integer>>) Class.forName(options.valueOf(className)).asSubclass(Callable.class);
Is there any way to get rid of that cast?
Using SE6.
Load class with forName() method in Java The class object associated with the class with the given string name can be returned with the method java. lang. Class. forName(String name, boolean initialize, ClassLoader loader), using the class loader that is used to load the class.
forName(String name, boolean initialize, ClassLoader loader) method returns the Class object associated with the class or interface with the given string name, using the given class loader. The specified class loader is used to load the class or interface.
class.forName() is a method in Java that returns the class object associated with the class or interface passed as the first parameter (i.e., name). This method is declared in the following way: ClassLoaders are responsible for loading classes into the memory.
The forName() method of java. lang. Class class is used to get the instance of this Class with the specified class name. This class name is specified as the string parameter.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With