我想使用其名称创建指定类的实例。我的代码如下所示。
我收到编译器警告。我这样做是对的吗?甚至可以使用类的名称并获取该类型的实例,因为我认为编译器没有任何方式知道该类型应该是什么?
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();
}
}
由于
答案 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;例如何时分配实例。
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;
}
}