Java 泛型和 Class.forName
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1733799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generics and Class.forName
提问by dogbane
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
谢谢
采纳答案by Stephen C
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.
顺便说一句,如果您忽略这些警告,则 create 方法可能会创建某个类的实例,该实例与调用者使用的实际类型不兼容。这很可能会导致稍后出现意外的 ClassCastException;例如,当实例被分配时。
EDIT: @Pascal points out that we need to add a typecast to make this compile; i.e.
编辑:@Pascal 指出我们需要添加一个类型转换来进行编译;IE
Class<T> clazz = (Class<T>) Class.forName(className).asSubclass(ifaceClass);
Unfortunately, we alsoneed to add a @SuppressWarnings annotation.
不幸的是,我们还需要添加一个@SuppressWarnings 注释。
回答by Bozho
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.
我认为这是因为Class.forName(..)
未针对 T 进行参数化。当您触发 eclipse 自动完成功能时,它假定 clazz.newInstance() 返回对象。因此,保留演员表并添加@SuppressWarnings。如果您没有正确使用该方法(即String str = Utils.create("java.util.ArrayList");
),ClassCastException
则会发生 a,但这是正常的。
回答by KLE
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
}
回答by chrisg
Even if you can get the code above to work, the newInstance() method of constructor assumes a zero argument constructor.
即使你可以让上面的代码工作,构造函数的 newInstance() 方法假设一个零参数构造函数。
If the Class does not have one i.e the zero argument constructor of the Class you are trying to create has been explicitly declared private, or package depending on where the method is called from, you will get an IllegalAccessException. Something to add to your precondition, if you get it working.
如果该类没有一个,即您尝试创建的类的零参数构造函数已显式声明为私有或包,具体取决于调用方法的位置,您将收到 IllegalAccessException。如果你让它工作,可以添加到你的先决条件中。
回答by meriton
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.
您不能将类型参数限制为包含名为 className 的类型。因此,调用者可以为您的 create(String) 函数提供任何类型,这当然不是类型安全的。
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.
由于您不能静态地强制返回的实例实现任何接口(无需调用者通过传递相应的 Class 对象来告诉您),因此我将放弃泛型,并将调用者强制转换为他期望的任何类型。
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);
回答by osexp2003
I found an interesting thing: Type
can be converted to Class
directly if it is not a generic type. But i still can not Class.forName("java.util.List<SomeType>")
.
我发现了一个有趣的事情:如果不是泛型类型,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;
}
}