在 Java 中使用反射创建一个新实例,并将引用变量类型设置为新实例类名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2215843/
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
Using reflection in Java to create a new instance with the reference variable type set to the new instance class name?
提问by
All the examples I look at for reflection show creating a new instance of an unknown implementation, and casting that implementation to it's interface. The issue with this is that now you can't call any new methods (only overrides) on the implementing class, as your object reference variable has the interface type. Here is what I have:
我查看的所有反射示例都显示了创建一个未知实现的新实例,并将该实现转换为它的接口。问题在于,现在您无法在实现类上调用任何新方法(仅覆盖),因为您的对象引用变量具有接口类型。这是我所拥有的:
Class c = null;
try {
c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
InterfaceType interfaceType = null;
try {
interfaceType = (InterfaceType)c.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
If I only have a reference to "com.path.to.ImplementationType", and I don't know what that type might be (it is coming from a config file), then how can I use the class name to cast it to ImplementationType? Is this even possible?
如果我只有对“com.path.to.ImplementationType”的引用,并且我不知道该类型可能是什么(它来自配置文件),那么我如何使用类名将其强制转换为实施类型?这甚至可能吗?
采纳答案by akf
This line seems to sum up the crux of your problem:
这一行似乎总结了您的问题的症结所在:
The issue with this is that now you can't call any new methods (only overrides) on the implementing class, as your object reference variable has the interface type.
问题在于,现在您无法在实现类上调用任何新方法(仅覆盖),因为您的对象引用变量具有接口类型。
You are pretty stuck in your current implementation, as not only do you have to attempt a cast, you also need the definition of the method(s) that you want to call on this subclass. I see two options:
您在当前的实现中陷入困境,因为您不仅必须尝试强制转换,还需要定义要在此子类上调用的方法。我看到两个选项:
1.As stated elsewhere, you cannot use the String representation of the Class name to cast your reflected instance to a known type. You can, however, use a String
equals()
test to determine whether your class is of the type that you want, and then perform a hard-coded cast:
1.如别处所述,您不能使用类名称的字符串表示将反射实例转换为已知类型。但是,您可以使用String
equals()
测试来确定您的类是否属于您想要的类型,然后执行硬编码转换:
try {
String className = "com.path.to.ImplementationType";// really passed in from config
Class c = Class.forName(className);
InterfaceType interfaceType = (InterfaceType)c.newInstance();
if (className.equals("com.path.to.ImplementationType") {
((ImplementationType)interfaceType).doSomethingOnlyICanDo();
}
} catch (Exception e) {
e.printStackTrace();
}
This looks pretty ugly, and it ruins the nice config-driven process that you have. I dont suggest you do this, it is just an example.
这看起来很丑陋,它破坏了您拥有的漂亮的配置驱动过程。我不建议你这样做,这只是一个例子。
2.Another option you have is to extend your reflection from just Class
/Object
creation to include Method
reflection. If you can create the Class
from a String passed in from a config file, you can also pass in a method name from that config file and, via reflection, get an instance of the Method
itself from your Class
object. You can then call invoke
(http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object, java.lang.Object...)) on the Method
, passing in the instance of your class that you created. I think this will help you get what you are after.
2.你的另一个选择是将你的反射从Class
/Object
创建扩展到包括Method
反射。如果您可以Class
从从配置文件传入的字符串创建,则还可以从该配置文件传入方法名称,并通过反射Method
从Class
对象中获取自身的实例。然后你可以调用invoke
(http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object, java.lang.Object...) ) 上Method
,传入您创建的类的实例。我认为这将帮助你得到你所追求的。
Here is some code to serve as an example. Note that I have taken the liberty of hard coding the params for the methods. You could specify them in a config as well, and would need to reflect on their class names to define their Class
obejcts and instances.
下面是一些代码作为示例。请注意,我冒昧地对方法的参数进行了硬编码。您也可以在配置中指定它们,并且需要反映它们的类名以定义它们的Class
对象和实例。
public class Foo {
public void printAMessage() {
System.out.println(toString()+":a message");
}
public void printAnotherMessage(String theString) {
System.out.println(toString()+":another message:" + theString);
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("Foo");
Method method1 = c.getDeclaredMethod("printAMessage", new Class[]{});
Method method2 = c.getDeclaredMethod("printAnotherMessage", new Class[]{String.class});
Object o = c.newInstance();
System.out.println("this is my instance:" + o.toString());
method1.invoke(o);
method2.invoke(o, "this is my message, from a config file, of course");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException nsme){
nsme.printStackTrace();
} catch (IllegalAccessException iae) {
iae.printStackTrace();
} catch (InstantiationException ie) {
ie.printStackTrace();
} catch (InvocationTargetException ite) {
ite.printStackTrace();
}
}
}
and my output:
和我的输出:
this is my instance:Foo@e0cf70
Foo@e0cf70:a message
Foo@e0cf70:another message:this is my message, from a config file, of course
回答by fastcodejava
If you knew the Class
of ImplementationType
you could create an instance of it. So what you are trying to do is not possible.
如果你知道Class
的ImplementationType
,你可以创建它的一个实例。所以你试图做的事情是不可能的。
回答by Kevin
You want to be able to pass in a Class and get a type-safe instance of that class? Try the following:
您希望能够传入一个类并获得该类的类型安全实例吗?请尝试以下操作:
public static void main(String [] args) throws Exception {
String s = instanceOf(String.class);
}
public static <T> T instanceOf (Class<T> clazz) throws Exception {
return clazz.newInstance();
}
回答by nanda
I'm not absolutely sure I got your question correctly, but it seems you want something like this:
我不确定我是否正确回答了您的问题,但您似乎想要这样的东西:
Class c = null;
try {
c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
T interfaceType = null;
try {
interfaceType = (T) c.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Where T can be defined in method level or in class level, i.e. <T extends InterfaceType>
其中 T 可以在方法级别或类级别定义,即 <T extends InterfaceType>
回答by user268396
As an addendum to akf's answer you could use instanceof checks instead of String equals() calls:
作为 akf 答案的附录,您可以使用 instanceof 检查而不是 String equals() 调用:
String cname="com.some.vendor.Impl";
try {
Class c=this.getClass().getClassLoader().loadClass(cname);
Object o= c.newInstance();
if(o instanceof Spam) {
Spam spam=(Spam) o;
process(spam);
}
else if(o instanceof Ham) {
Ham ham = (Ham) o;
process(ham);
}
/* etcetera */
}
catch(SecurityException se) {
System.err.printf("Someone trying to game the system?%nOr a rename is in order because this JVM doesn't feel comfortable with: “%s”", cname);
se.printStackTrace();
}
catch(LinkageError le) {
System.err.printf("Seems like a bad class to this JVM: “%s”.", cname);
le.printStackTrace();
}
catch(RuntimeException re) {
// runtime exceptions I might have forgotten. Classloaders are wont to produce those.
re.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
}
Note the liberal hardcoding of some values. Anyways the main points are:
注意一些值的自由硬编码。无论如何,要点是:
- Use instanceof rather than equals(). If anything, it will co-operate better when refactoring.
- Be sure to catch these runtime errors and security ones too.
- 使用 instanceof 而不是 equals()。如果有的话,它会在重构时更好地协作。
- 一定要捕获这些运行时错误和安全错误。
回答by GoToHyman
//====Single Class Reference used to retrieve object for fields and initial values. Performance enhancing only====
Class<?> reference = vector.get(0).getClass();
Object obj = reference.newInstance();
Field[] objFields = obj.getClass().getFields();