Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typesafe forName class loading

Tags:

java

generics

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.

like image 243
9 revs Avatar asked Jun 01 '11 07:06

9 revs


People also ask

How do I load a class using forName?

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.

What is class forName () does and how it is useful?

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.

What is class forName ()?

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.

What is the use of forName method in JDBC?

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.


1 Answers

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.

like image 109
irreputable Avatar answered Oct 14 '22 14:10

irreputable