Reputation: 763
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.
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.
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:
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.
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;
}
Upvotes: 2
Views: 18547
Reputation: 101
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.
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;
}
Upvotes: 1
Reputation: 781
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:
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.
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...?
You can't cast from Class
to Person
, since Person
is probably not a subclass of Class
.
Instead, declare your list as a List<Person>
instead of a List<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.
personList.add((Person)parentClassInstance);
And the loop at the bottom will need to change too:
for (Person person : personList) {
System.out.println(person);
}
Upvotes: 1
Reputation: 691765
The message explains what's wrong: componentClass is not an instance of PersonName. It's an object of type Class
(probably Class<PersonName>
). You probably forgot to instantiate the class.
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);
Upvotes: 3
Reputation: 70909
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 declared it to be a Class<?>
!
for (Class<?> class1 : personClassList) {
Class<Person> person = (Class<Person>) class1;
System.out.println(person);
}
Upvotes: 0