Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics and Class.forName

I would like to create an instance of a specified class using its name. My code is shown below.

I get a compiler warning. Am I doing this the right way? Is it even possible to use the name of a class and get an instance of that type back, as I don't think there is any way of the compiler knowing what the type should be?

public static <T> T create(final String className) {
    try {
        final Class<?> clazz = Class.forName(className);

        //WARNING: Type safety: Unchecked cast from capture#2-of ? to T
        return (T) create(clazz); 
    }
    catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

public static <T> T create(final Class<T> classToCreate) {
    final Constructor<T> constructor;
    try {
        constructor = classToCreate.getDeclaredConstructor();
        final T result = constructor.newInstance();
        return result;
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

Thanks

like image 838
dogbane Avatar asked Nov 14 '09 09:11

dogbane


People also ask

What does class forName do?

forName. Returns the Class object associated with the class or interface with the given string name, using the given class loader. Given the fully qualified name for a class or interface (in the same format returned by getName ) this method attempts to locate, load, and link the class or interface.

What is a generics class?

A Generic class simply means that the items or functions in that class can be generalized with the parameter(example T) to specify that we can add any type as a parameter in place of T like Integer, Character, String, Double or any other user-defined type.

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. A solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

What is generic class and method?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.


Video Answer


4 Answers

I think that the first method should look something like this:

public static <T> T create(final String className, Class<T> ifaceClass) 
throws ClassNotFoundException {
    final Class<T> clazz = Class.forName(className).asSubclass(ifaceClass);
    return create(clazz); 
}

You cannot do an up-cast typecast using a type parameter ... without those pesky type-safety warnings.

By the way, if you ignore those warnings, the create method may create an instance of some class that isn't compatible with the actual type used by the caller. This is likely to lead to an unexpected ClassCastException later on; e.g. when the instance is assigned.


EDIT: @Pascal points out that we need to add a typecast to make this compile; i.e.

Class<T> clazz = (Class<T>) Class.forName(className).asSubclass(ifaceClass);

Unfortunately, we also need to add a @SuppressWarnings annotation.

like image 128
Stephen C Avatar answered Oct 23 '22 08:10

Stephen C


I think this is because Class.forName(..) isn't parameterized for T. When you trigger the eclipse autocomplete, it assumes the clazz.newInstance() return Object. So, retain the cast and add @SuppressWarnings. If you don't use the method properly (i.e. String str = Utils.create("java.util.ArrayList");) then a ClassCastException will occur, but that is normal.

like image 31
Bozho Avatar answered Oct 23 '22 09:10

Bozho


The second method is fine.


But for the first one, any class name could be passed as a parameter.

The point of the method would be to instanciate a class that the calling code doesn't know at compile-time, it only knows about it at runtime.

In these conditions, the calling code cannot set the Type Parameter, so the method cannot be parameterized, so the cast has no point...

Therefore, I would say the whole method has no point.


Some point could be given to the method if the calling code would know a supertype of the class. That could be passed as a parameter, and the cast would be possible and meaningful.

public static <T> T create(final Class<T> superType, final String className) {
  try {
    final Class<?> clazz = Class.forName(className);
    final Object object = clazz.newInstance();
    if (superType.isInstance(object)) {
      return (T)object; // safe cast
    }
    return null; // or other error 
  } // catch and other error code
}
like image 2
KLE Avatar answered Oct 23 '22 08:10

KLE


You can not restrict a type parameter to contain the type named className. Hence, a caller can supply any type to your create(String) function, which is of course not type safe.

Since you cannot statically enforce that the returned instance implements any interface (without having the caller tell you by passing the corresponding Class object), I'd dispense with generics, and have the caller cast to whatever type he expects.

public static Object create(final String className) {
    try {
        final Class<?> clazz = Class.forName(className);
        return create(clazz); 
    }
    catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

A caller can then write:

Foo foo = (Foo) create("my.cool.FooBar");

as opposed to

Foo foo = create("my.cool.FooBar", Foo.class);
like image 1
meriton Avatar answered Oct 23 '22 08:10

meriton