泛型和Class.forName

时间:2009-11-14 09:29:18

标签: java generics instantiation

我想使用其名称创建指定类的实例。我的代码如下所示。

我收到编译器警告。我这样做是对的吗?甚至可以使用类的名称并获取该类型的实例,因为我认为编译器没有任何方式知道该类型应该是什么?

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();
    }
}

由于

6 个答案:

答案 0 :(得分:30)

我认为第一种方法应该是这样的:

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); 
}

你不能使用类型参数进行上传类型转换...没有那些讨厌的类型安全警告。

顺便说一句,如果忽略这些警告,create方法可能会创建某个类的实例,该实例与调用者使用的实际类型不兼容。这可能会在以后导致意外的ClassCastException;例如何时分配实例。


编辑:@Pascal指出我们需要添加一个类型转换来进行编译;即。

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

不幸的是,我们需要添加@SuppressWarnings注释。

答案 1 :(得分:2)

我认为这是因为Class.forName(..)未针对T进行参数化。当您触发eclipse自动完成时,它假定clazz.newInstance()返回Object。因此,保留演员表并添加@SuppressWarnings。如果您没有正确使用该方法(即String str = Utils.create("java.util.ArrayList");),则会出现ClassCastException,但这是正常的。

答案 2 :(得分:2)

第二种方法很好。


但是对于第一个,任何类名都可以作为参数传递。

该方法的目的是实现一个调用代码在编译时不知道的类,它只在运行时知道它。

在这些条件下,调用代码无法设置类型参数,因此该方法无法参数化,因此强制转换没有意义......

因此,我会说整个方法没有意义


如果调用代码知道类的超类型,可以给方法一些观点。这可以作为参数传递,并且演员阵容是可能且有意义的。

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
}

答案 3 :(得分:1)

您不能将类型参数限制为包含名为className的类型。因此,调用者可以为您的create(String)函数提供任何类型,这当然不是类型安全的。

由于您无法静态强制返回的实例实现任何接口(没有调用者通过传递相应的Class对象告诉您),我将免除泛型,并将调用者强制转换为他期望的任何类型。

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

然后呼叫者可以写:

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

而不是

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

答案 4 :(得分:0)

即使您可以使上面的代码工作,构造函数的newInstance()方法也假定零参数构造函数。

如果Class没有一个,即您尝试创建的Class的零参数构造函数已经显式声明为private,或者根据调用方法的位置而打包,则会出现IllegalAccessException。如果让它发挥作用,可以添加一些前提条件。

答案 5 :(得分:0)

我发现了一件有趣的事情:Type如果不是通用类型,可以直接转换为Class。但我仍然不能Class.forName("java.util.List<SomeType>")

import java.lang.reflect.*;
import java.util.*;

public class Main {
    public void func(List<List<String>> listlist, Map<String, List<String>> list, String s) {}

    public static void main(String[] args) throws ClassNotFoundException {
        Method m = Main.class.getDeclaredMethods()[1];

        Type t0 = m.getGenericParameterTypes()[0];
        boolean b0 = t0 instanceof Class;

        boolean b01 = ((ParameterizedType)t0).getRawType() instanceof Class;

        Type t1 = m.getGenericParameterTypes()[2];
        boolean b1 = t1 instanceof Class;
    }
}

enter image description here