使用 Java 反射的动态转换
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18001824/
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
Dynamic Casting Using Java Reflection
提问by Amar
I have two classes, as follows:
我有两个类,如下:
public class Person {
private String dob;
private PersonName personName;
}
public class PersonName {
private String firstName;
private String lastName;
}
I am setting these values dynamically using Java Reflection.
我正在使用 Java 反射动态设置这些值。
First, I create an instance of Person
and I set the value for dob
. After that, I need to set a PersonName
value in Person
. So I created another instance of PersonName
and I set the values in that PersonName
. After that, I am trying to set the PersonName
instance in the Person
entity.
首先,我创建了一个实例Person
并设置了 的值dob
。之后,我需要PersonName
在Person
. 所以我创建了另一个实例,PersonName
并在其中设置了值PersonName
。之后,我试图PersonName
在Person
实体中设置实例。
For that I used code like this:
为此,我使用了这样的代码:
Class componentClass = Class.forName(clazz.getName());
Field field = parentClass.getDeclaredField(Introspector
.decapitalize(clazz.getSimpleName()));
field.setAccessible(true);
field.set(parentClass, componentClass);
Here, parentClass
is a Person
instance and componentClass
is a PersonName
instance. I am trying to set the PersonName
in the Person
, but I am getting the following exception:
这里,parentClass
是一个Person
实例,componentClass
是一个PersonName
实例。我想设置PersonName
的Person
,但我得到以下异常:
java.lang.IllegalArgumentException: Can not set com.rise.common.model.PersonName field
com.rise.common.model.Person.personName to java.lang.Class
So how can I set the values dynamically?
那么如何动态设置值呢?
Thanks.
谢谢。
My Whole Code:
我的整个代码:
protected void assignProperties(List<Object[]> argResults,
List<Class> argAllClassesList, Class argParentClass)
throws ClassNotFoundException, NoSuchFieldException,
SecurityException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException, InstantiationException {
List<Object[]> results = (List<Object[]>) Precondition.ensureNotEmpty(
argResults, "Output List");
List<Class<?>> personClassList = new ArrayList<Class<?>>();
for (Object[] recordValues : results) {
Class parentClass = Class.forName(this.getPersistentClass()
.getName());
parentClass.newInstance();
int count = 0;
count = assignValues(recordValues, parentClass, count);
for (Class clazz : argAllClassesList) {
Class componentClass = Class.forName(clazz.getName());
componentClass.newInstance();
String decapitalize = Introspector.decapitalize(clazz
.getSimpleName());
Field field = parentClass.getDeclaredField(decapitalize);
field.setAccessible(true);
assignValues(recordValues, componentClass, count);
field.set(parentClass, componentClass);
}
personClassList.add(parentClass);
}
for (Class<?> class1 : personClassList) {
Class<Person> person = (Class<Person>) class1;
System.out.println(person);
}
}
private int assignValues(Object[] argRecordValues, Class argClass,
int argCount) {
String paramName = Introspector.decapitalize(argClass.getSimpleName());
if (Precondition.checkNotEmpty(paramName)) {
List<Field> fieldNames = TenantConfigHelper.getInstance()
.getModelNameVsFieldsMap().get(paramName);
try {
for (Field field : fieldNames) {
BeanUtils.setProperty(argClass, field.getName(),
argRecordValues[argCount]);
++argCount;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return argCount;
}
采纳答案by user506069
I think there may be some confusion over the difference between Java class definitions and instances at work here. You want to set the values of fields on particular instances, not the classes themselves. Something like this may work:
我认为这里的 Java 类定义和实例之间的区别可能有些混淆。您想在特定实例上设置字段的值,而不是类本身。像这样的事情可能会奏效:
Object parentClassInstance = parentClass.newInstance();
Class componentClass = Class.forName(clazz.getName());
Object componentClassInstance = componentClass.newInstance();
Field field = parentClass.getDeclaredField(Introspector
.decapitalize(clazz.getSimpleName()));
field.setAccessible(true);
field.set(parentClassInstance, componentClassInstance);
Looking at the whole code sample, however, it is a little hard to follow. Why have a List
of Class
es, with a name like personClassList
, which would seem to indicate that each class should be the same class, Person
? I feel it should probably instead be a List<Person>
or perhaps List<Object>
, which you would populate with your Person
instances, not the Class
objects themselves.
然而,查看整个代码示例,有点难以理解。为什么拥有List
的Class
ES,与像一个名字personClassList
,这似乎表明,每个类应该是同一类Person
?我觉得它可能应该改为 aList<Person>
或者也许List<Object>
,你会用你的Person
实例填充它,而不是Class
对象本身。
Edit to answer the following question in a comment:
编辑以在评论中回答以下问题:
I have to return List instances insetad of List so how can I type case from Class to Person dynamically...?
我必须返回 List 的 List 实例 insetad 所以我怎样才能动态地从 Class 到 Person 键入 case ......?
You can't cast from Class
to Person
, since Person
is probably not a subclass of Class
.
您不能从Class
to强制转换Person
,因为Person
可能不是Class
.
Instead, declare your list as a List<Person>
instead of a List<Class<?>>
相反,将您的列表声明为 aList<Person>
而不是 aList<Class<?>>
List<Person> personList = new ArrayList<Person>();
Then add the Person
objects instead of the Class
objects to your list at the bottom of your first for
loop.
然后将Person
对象而不是Class
对象添加到第一个for
循环底部的列表中。
personList.add((Person)parentClassInstance);
And the loop at the bottom will need to change too:
底部的循环也需要改变:
for (Person person : personList) {
System.out.println(person);
}
回答by JB Nizet
The message explains what's wrong: componentClass is not an instanceof PersonName. It's an object of type Class
(probably Class<PersonName>
). You probably forgot to instantiate the class.
该消息说明了问题所在:componentClass 不是PersonName的实例。它是一个类型的对象Class
(可能是Class<PersonName>
)。您可能忘记实例化该类。
Edit:
编辑:
Your code does:
你的代码做:
parentClass.newInstance();
and
和
componentClass.newInstance();
This is the equivalent of doing
这相当于做
new Parent();
and
和
new ParentName();
So it creates an instance, but doesn't assign it to any variable, and thus doesn't do anything with the created instance, which will be garbage-collectable immediately.
所以它创建了一个实例,但不将它分配给任何变量,因此不会对创建的实例做任何事情,这将立即被垃圾收集。
You want
你要
Object parent = parentClass.newInstance();
Object component = componentClass.newInstance();
field.set(parent, component);
回答by Edwin Buck
The type system is verified by the compiler. While there is some additional safety possible during runtime, that additional safety is not even close to the safety the compiler imposes. Which begs the question, Why?
类型系统由编译器验证。虽然在运行时可能存在一些额外的安全性,但这种额外的安全性甚至与编译器强加的安全性相差甚远。这引出了一个问题,为什么?
Assuming you could get the stuff to work, exactly how would you be able to justify casting class1
below into a Class<Person>
type? You declaredit to be a Class<?>
!
假设你可以让这些东西工作,你究竟如何证明将class1
下面的转换为Class<Person>
类型?你宣布它是一个Class<?>
!
for (Class<?> class1 : personClassList) {
Class<Person> person = (Class<Person>) class1;
System.out.println(person);
}
回答by PaperBeatsRock-PFFFT
I was looking for this same type of answer and basically what everyone is specifying with the Object is correct. I also created a generic list like List<Object>
instead of the actual class name. Below is a function that returns a List of type Object's. I pass in the class name and load that with the .loadClass. Than create a new object with that new class instance with the .newInstance. The dList gets loaded with all the information for each objectClass which is the class I pass in with the className variable. The rest is basically just dynamically invoking all of the "set" methods within that particular class with the values from the result set.
我一直在寻找相同类型的答案,基本上每个人用 Object 指定的内容都是正确的。我还创建了一个通用列表,List<Object>
而不是实际的类名。下面是一个返回对象类型列表的函数。我传入类名并使用 .loadClass 加载它。然后使用 .newInstance 使用该新类实例创建一个新对象。dList 加载了每个 objectClass 的所有信息,这是我使用 className 变量传入的类。其余的基本上只是使用结果集中的值动态调用该特定类中的所有“设置”方法。
protected List<Object> FillObject(ResultSet rs, String className)
{
List<Object> dList = new ArrayList<Object>();
try
{
ClassLoader classLoader = GenericModel.class.getClassLoader();
while (rs.next())
{
Class reflectionClass = classLoader.loadClass("models." + className);
Object objectClass = reflectionClass.newInstance();
Method[] methods = reflectionClass.getMethods();
for(Method method: methods)
{
if (method.getName().indexOf("set") > -1)
{
Class[] parameterTypes = method.getParameterTypes();
for(Class pT: parameterTypes)
{
Method setMethod = reflectionClass.getMethod(method.getName(), pT);
switch(pT.getName())
{
case "int":
int intValue = rs.getInt(method.getName().replace("set", ""));
setMethod.invoke(objectClass, intValue);
break;
case "java.util.Date":
Date dateValue = rs.getDate(method.getName().replace("set", ""));
setMethod.invoke(objectClass, dateValue);
break;
case "boolean":
boolean boolValue = rs.getBoolean(method.getName().replace("set", ""));
setMethod.invoke(objectClass, boolValue);
break;
default:
String stringValue = rs.getString(method.getName().replace("set", ""));
setMethod.invoke(objectClass, stringValue);
break;
}
}
}
}
dList.add(objectClass);
}
}
catch (Exception e)
{
this.setConnectionMessage("ERROR: reflection class loading: " + e.getMessage());
}
return dList;
}