Invoking all setters within a class using reflection - java

I have a domain object, that for the purposes of this question I will call Person with the following private variables:
String name
int age
Each of these have getters and setters. Now I also have a Map<String, String> with the following entries:
name, phil
age, 35
I would like to populate a list of all setter methods within the class Person and then looping through this list and invoking each method using the values from the map.
Is this even possible as I cannot see any examples close to this on the net. Examples are very much appreciated.

Sure it's possible! You can get all methods that start with "set" back by doing this:
Class curClass = myclass.class;
Method[] allMethods = curClass.getMethods();
List<Method> setters = new ArrayList<Method>();
for(Method method : allMethods) {
if(method.getName().startsWith("set")) {
setters.add(method);
}
}
Now you've got the methods. Do you already know how to call them for your instance of the class?

Have you tried BeanUtils.populate()) from Apache Commons BeanUtils?
BeanUtils.populate(yourObject, propertiesMap);

This is a full solution that verifies output class beforehand and consequently calls setters for all the properties that the map contains. It uses purely java.beans and java.lang.reflect.
public Object mapToObject(Map<String, Object> input, Class<?> outputType) {
Object outputObject = null;
List<PropertyDescriptor> outputPropertyDescriptors = null;
// Test if class is instantiable with default constructor
if(isInstantiable(outputType)
&& hasDefaultConstructor(outputType)
&& (outputPropertyDescriptors = getPropertyDescriptors(outputType)) != null) {
try {
outputObject = outputType.getConstructor().newInstance();
for(PropertyDescriptor pd : outputPropertyDescriptors) {
Object value = input.get(pd.getName());
if(value != null) {
pd.getWriteMethod().invoke(outputObject, value);
}
}
} catch (InstantiationException|IllegalAccessException|InvocationTargetException|NoSuchMethodException e) {
throw new IllegalStateException("Failed to instantiate verified class " + outputType, e);
}
} else {
throw new IllegalArgumentException("Specified outputType class " + outputType + "cannot be instantiated with default constructor!");
}
return outputObject;
}
private List<PropertyDescriptor> getPropertyDescriptors(Class<?> outputType) {
List<PropertyDescriptor> propertyDescriptors = null;
try {
propertyDescriptors = Arrays.asList(Introspector.getBeanInfo(outputType, Object.class).getPropertyDescriptors());
} catch (IntrospectionException e) {
}
return propertyDescriptors;
}
private boolean isInstantiable(Class<?> clazz) {
return ! clazz.isInterface() && ! Modifier.isAbstract(clazz.getModifiers());
}
private boolean hasDefaultConstructor(Class<?> clazz) {
try {
clazz.getConstructor();
return true;
} catch (NoSuchMethodException e) {
return false;
}
}

I think you could use a library, the Apache Commons BeanUtils. If you have a map that contains field and value pairs, the class PropertyUtils can help you:
Person person = new Person();
for(Map.Entry<String, Object> entry : map.entrySet())
PropertyUtils.setProperty(person, entry.getKey(), entry.getValue());

Related

How to create map of java POJO class/ Json String having primitive data?

I want to make a Map (String ,Object) like this
{AssessmentId=0, Physical_name='ram', Physical_height=20, Physical_weight=60}
from my Pojo Class - InitialAssessment
public class InitialAssessment {
private long AssessmentId;
private String physical_name;
private String physical_gender;
private int physical_height;
private float physical_weight;
// all getter And setter is Created here
}
without using any external Library like Gson etc.
You can use this approach:
public Map getMapFromPojo(InitialAssessment assessment) throws Exception {
Map<String, Object> map = new HashMap<>();
if (assessment != null) {
Method[] methods = assessment.getClass().getMethods();
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("get") && !name.equalsIgnoreCase("getClass")) {
Object value = "";
try {
value = method.invoke(assessment);
map.put(name.substring(name.indexOf("get") + 3), value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return map;
}
return null;
}
It will give you map for pojo class like this:
Output:
{AssessmentId=0, Physical_name='ram', Physical_gender='Male' , Physical_height=20, Physical_weight=60}

Copy object properties by direct field access

Is there an easy way to copy an object's property's onto another object of a different class which has the same field names using direct field access - i.e. when one of the classes does not have getters or setters for the fields? I can use org.springframework.beans.BeanUtils#copyProperties(Object source, Object target) when they both have getter and setter methods, but what can I do when they don't?
It may also be relevant that the fields are public.
I know that I can write my own code to do this using reflection, but I'm hoping that there's some library that provides a one-liner.
I didn't find a 3rd-party library to do this quite how I wanted. I'll paste my code here in case it is useful to anyone:
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An alternative to Spring's BeanUtils#copyProperties for classes that don't have getters and setters.
*/
public class FieldCopier {
private static final Logger log = LoggerFactory.getLogger(FieldCopier.class);
/** Always use the same instance, so that we can cache the fields. */
private static final FieldCopier instance = new FieldCopier();
/** Caching the paired fields cuts the time taken by about 25% */
private final Map<Map.Entry<Class<?>, Class<?>>, Map<Field, Field>> PAIRED_FIELDS = new ConcurrentHashMap<>();
/** Caching the fields cuts the time taken by about 50% */
private final Map<Class<?>, Field[]> FIELDS = new ConcurrentHashMap<>();
public static FieldCopier instance() {
return instance;
}
private FieldCopier() {
// do not instantiate
}
public <S, T> T copyFields(S source, T target) {
Map<Field, Field> pairedFields = getPairedFields(source, target);
for (Field sourceField : pairedFields.keySet()) {
Field targetField = pairedFields.get(sourceField);
try {
Object value = getValue(source, sourceField);
setValue(target, targetField, value);
} catch(Throwable t) {
throw new RuntimeException("Failed to copy field value", t);
}
}
return target;
}
private <S, T> Map<Field, Field> getPairedFields(S source, T target) {
Class<?> sourceClass = source.getClass();
Class<?> targetClass = target.getClass();
Map.Entry<Class<?>, Class<?>> sourceToTarget = new AbstractMap.SimpleImmutableEntry<>(sourceClass, targetClass);
PAIRED_FIELDS.computeIfAbsent(sourceToTarget, st -> mapSourceFieldsToTargetFields(sourceClass, targetClass));
Map<Field, Field> pairedFields = PAIRED_FIELDS.get(sourceToTarget);
return pairedFields;
}
private Map<Field, Field> mapSourceFieldsToTargetFields(Class<?> sourceClass, Class<?> targetClass) {
Map<Field, Field> sourceFieldsToTargetFields = new HashMap<>();
Field[] sourceFields = getDeclaredFields(sourceClass);
Field[] targetFields = getDeclaredFields(targetClass);
for (Field sourceField : sourceFields) {
if (sourceField.getName().equals("serialVersionUID")) {
continue;
}
Field targetField = findCorrespondingField(targetFields, sourceField);
if (targetField == null) {
log.warn("No target field found for " + sourceField.getName());
continue;
}
if (Modifier.isFinal(targetField.getModifiers())) {
log.warn("The target field " + targetField.getName() + " is final, and so cannot be written to");
continue;
}
sourceFieldsToTargetFields.put(sourceField, targetField);
}
return Collections.unmodifiableMap(sourceFieldsToTargetFields);
}
private Field[] getDeclaredFields(Class<?> clazz) {
FIELDS.computeIfAbsent(clazz, Class::getDeclaredFields);
return FIELDS.get(clazz);
}
private <S> Object getValue(S source, Field sourceField) throws IllegalArgumentException, IllegalAccessException {
sourceField.setAccessible(true);
return sourceField.get(source);
}
private <T> void setValue(T target, Field targetField, Object value) throws IllegalArgumentException, IllegalAccessException {
targetField.setAccessible(true);
targetField.set(target, value);
}
private Field findCorrespondingField(Field[] targetFields, Field sourceField) {
for (Field targetField : targetFields) {
if (sourceField.getName().equals(targetField.getName())) {
if (sourceField.getType().equals(targetField.getType())) {
return targetField;
} else {
log.warn("Different types for field " + sourceField.getName()
+ " source " + sourceField.getType() + " and target " + targetField.getType());
return null;
}
}
}
return null;
}
}
Write a simple utility class for that and you got your one liner... this task is IMHO to easy to use a library for it.
Just keep in mind to make your fields accessible if they aren't by default. Here are two functions you could adapt from our codebase:
public void injectIntoObject(Object o, Object value) {
try {
getField().set(o, value);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Illegal argument while injecting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'. Got one of type "+value.getClass().getCanonicalName()+" but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e) {
getField().setAccessible(true);
try {
getField().set(o, value);
} catch (IllegalArgumentException e1) {
throw new RuntimeException("Illegal argument while injecting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'. Got one of type "+value.getClass().getCanonicalName()+" but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e1) {
throw new RuntimeException("Access exception while injecting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'!",e);
}
} catch (Exception e) {
throw new RuntimeException("Exception while setting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'!",e);
}
}
public Object extractFromObject(Object o) {
try {
return getField().get(o);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Illegal argument while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e) {
getField().setAccessible(true);
try {
return getField().get(o);
} catch (IllegalArgumentException e1) {
throw new RuntimeException("Illegal argument while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e1) {
throw new RuntimeException("Access exception while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"'!",e);
}
} catch (Exception e) {
throw new RuntimeException("Exception while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"'!",e);
}
}
getField() returns a java.lang.Field, should be easy to implement.
I would strongly suggest that you avoid using reflection for this, as it leads to code that is difficult to understand and maintain. (Reflection is ok for testing and when creating frameworks, other than this it probably creates more problems than it solves.)
Also, if a property of an object needs to be accessed by something other than the object, it needs a scope that is not private (or an accessor/getter that is not private). That is the whole point of variable scopes. Keeping a variable private without accessors, and then using it anyways through reflection is just wrong, and will just lead to problems, as you are creating code that lies to the reader.
public class MyClass {
private Integer someInt;
private String someString;
private List<Double> someList;
//...
}
public class MyOtherClass {
private Integer someInt;
private String someString;
private List<Double> someList;
private boolean somethingElse;
public copyPropertiesFromMyClass(final MyClass myClass) {
this.someInt = myClass.getSomeInt();
this.someString = myClass.getSomeString();
this.someList = new ArrayList<>(myClass.getSomeList());
}
}

How can I generate a map of all initialized variables from an object in Java?

I have a large list of java objects that all inherit from one shared object and each contains many field members (properties). However, not all fields on all of the objects are guaranteed to be initialized. There are also fields that are contained in the super class which should be included as well. I am looking to create a map that contains all of the initialized members with the member's identifier as the key and their value and the map value.
Is this even possible? I have looked briefly into reflection which looked promising but I do not have much experience with it. All of the values are primitive value types and could be stored in strings if necessary.
Use this
public void method method(Object obj) {
Map initializedFieldsMap = new HashMap();
for (Field field : obj.getClass().getDeclaredFields()) {
Boolean acessibleState = field.isAccessible();
field.setAccessible(true);
Object value;
try {
value = field.get(obj);
if (value != null) {
initializedFieldsMap.put(field.getName(), new WeakReference(value));
}
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
field.setAccessible(acessibleState);
}
return initializedFieldsMap;
}
It's using a WeakReference here so the object value won't get "stucked" (but it's still not ideal) and ineligible to GC, to access a value (String for instance) from the Map use:
String xxx = (String)map.get("value").get();
Combining the answers that I found:
public static Map<String, String> generatePropertiesMap(Object o)
{
Map<String, String> properties = new HashMap<String, String>();
for (Field field : getAllDeclaredFields(o)) {
Boolean acessibleState = field.isAccessible();
field.setAccessible(true);
Object value;
try {
value = field.get(o);
if (value != null) {
properties.put(field.getName(), value.toString());
}
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
field.setAccessible(acessibleState);
}
return properties;
}
private static List<Field> getAllDeclaredFields(Object o) {
List<Field> list = new ArrayList<Field>();
List<Field[]> fields = new ArrayList<Field[]>();
//work up from this class until Object
Class next = o.getClass();
while (true) {
Field[] f = next.getDeclaredFields();
fields.add(f);
next = next.getSuperclass();
if (next.equals(Object.class))
break;
}
for (Field[] f : fields) {
list.addAll(Arrays.asList(f));
}
return list;
}

Java—how can I dynamically reference an object's property?

In javascript, I can do this:
function MyObject(obj) {
for (var property in obj) {
this[property] = obj[property];
}
}
Can I do anything close in Java?
class MyObject {
String myProperty;
public MyObject(HashMap<String, String> props) {
// for each key in props where the key is also the name of
// a property in MyObject, can I assign the value to this.[key]?
}
}
Not that I disagree with Joel's answer, but I do not think it is not quite that difficult, if you essentially just want a best effort. Essentially check if it is there, and if it is try to set. If it works great if not, oh well we tried. For example:
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class MyObject {
protected String lorem;
protected String ipsum;
protected int integer;
public MyObject(Map<String, Object> valueMap){
for (String key : valueMap.keySet()){
setField(key, valueMap.get(key));
}
}
private void setField(String fieldName, Object value) {
Field field;
try {
field = getClass().getDeclaredField(fieldName);
field.set(this, value);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Map<String, Object> valueMap = new HashMap<String, Object>();
valueMap.put("lorem", "lorem Value");
valueMap.put("ipsum", "ipsum Value");
valueMap.put("integer", 100);
valueMap.put("notThere", "Nope");
MyObject f = new MyObject(valueMap);
System.out.println("lorem => '"+f.lorem+"'");
System.out.println("ipsum => '"+f.ipsum+"'");
System.out.println("integer => '"+f.integer+"'");
}
}
Yes, you can do it by reflection with something along the following lines:
/**
* Returns a list of all Fields in this object, including inherited fields.
*/
private List<Field> getFields() {
List<Field> list = new ArrayList<Field>();
getFields(list, getClass());
return list;
}
/**
* Adds the fields of the provided class to the List of Fields.
* Recursively adds Fields also from super classes.
*/
private List<Field> getFields(List<Field> list, Class<?> startClass) {
for (Field field : startClass.getDeclaredFields()) {
list.add(field);
}
Class<?> superClass = startClass.getSuperclass();
if(!superClass.equals(Object.class)) {
getFields(list, superClass);
}
}
public void setParameters(Map<String, String> props) throws IllegalArgumentException, IllegalAccessException {
for(Field field : getFields()) {
if (props.containsKey(field.getName())) {
boolean prevAccessible = field.isAccessible();
if (!prevAccessible) {
/*
* You're not allowed to modify this field.
* So first, you modify it to make it modifiable.
*/
field.setAccessible(true);
}
field.set(this, props.get(field.getName()));
/* Restore the mess you made */
field.setAccessible(prevAccessible);
}
}
}
However, if you are not very familiar with Java, this approach should be avoided if at all possible, as it is somewhat dangerous and error prone. For instance, there is no guarantee that the Field you are attempting to set are actually expecting a String. If it is the case that they are not, your program will crash and burn.
First, I would use a map if at all possible:
class MyObject {
// String myProperty; // ! not this
HashMap<String,String> myProperties; // use this instead
}
but let's say you wanted to set the fields dynamically.
public MyObject(HashMap<String, String> props) {
for (Map.Entry<String,String> entry : props.entrySet()) {
Field field = this.getClass().getField(entry.getKey());
field.set(this, entry.getValue());
}
}
of course, you will want to use a try/catch in the above constructor.
Well, if you really want to go down the reflection raod, then I suggest to have a look at the Introspector class and get the list of PropertyDescriptors from the BeanInfo.

Any way to Invoke a private method?

I have a class that uses XML and reflection to return Objects to another class.
Normally these objects are sub fields of an external object, but occasionally it's something I want to generate on the fly. I've tried something like this but to no avail. I believe that's because Java won't allow you to access private methods for reflection.
Element node = outerNode.item(0);
String methodName = node.getAttribute("method");
String objectName = node.getAttribute("object");
if ("SomeObject".equals(objectName))
object = someObject;
else
object = this;
method = object.getClass().getMethod(methodName, (Class[]) null);
If the method provided is private, it fails with a NoSuchMethodException. I could solve it by making the method public, or making another class to derive it from.
Long story short, I was just wondering if there was a way to access a private method via reflection.
You can invoke private method with reflection. Modifying the last bit of the posted code:
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);
There are a couple of caveats. First, getDeclaredMethod will only find method declared in the current Class, not inherited from supertypes. So, traverse up the concrete class hierarchy if necessary. Second, a SecurityManager can prevent use of the setAccessible method. So, it may need to run as a PrivilegedAction (using AccessController or Subject).
Use getDeclaredMethod() to get a private Method object and then use method.setAccessible() to allow to actually call it.
If the method accepts non-primitive data type then the following method can be used to invoke a private method of any class:
public static Object genericInvokeMethod(Object obj, String methodName,
Object... params) {
int paramCount = params.length;
Method method;
Object requiredObj = null;
Class<?>[] classArray = new Class<?>[paramCount];
for (int i = 0; i < paramCount; i++) {
classArray[i] = params[i].getClass();
}
try {
method = obj.getClass().getDeclaredMethod(methodName, classArray);
method.setAccessible(true);
requiredObj = method.invoke(obj, params);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return requiredObj;
}
The Parameter accepted are obj, methodName and the parameters. For example
public class Test {
private String concatString(String a, String b) {
return (a+b);
}
}
Method concatString can be invoked as
Test t = new Test();
String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");
you can do this using ReflectionTestUtils of Spring (org.springframework.test.util.ReflectionTestUtils)
ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);
Example : if you have a class with a private method square(int x)
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
Let me provide complete code for execution protected methods via reflection. It supports any types of params including generics, autoboxed params and null values
#SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}
public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
return executeMethod(instance.getClass(), instance, methodName, params);
}
#SuppressWarnings("unchecked")
public static <T> T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception {
Method[] allMethods = clazz.getDeclaredMethods();
if (allMethods != null && allMethods.length > 0) {
Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new);
for (Method method : allMethods) {
String currentMethodName = method.getName();
if (!currentMethodName.equals(methodName)) {
continue;
}
Type[] pTypes = method.getParameterTypes();
if (pTypes.length == paramClasses.length) {
boolean goodMethod = true;
int i = 0;
for (Type pType : pTypes) {
if (!ClassUtils.isAssignable(paramClasses[i++], (Class<?>) pType)) {
goodMethod = false;
break;
}
}
if (goodMethod) {
method.setAccessible(true);
return (T) method.invoke(instance, params);
}
}
}
throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " +
Arrays.toString(paramClasses));
}
throw new MethodNotFoundException("There are no methods found with name " + methodName);
}
Method uses apache ClassUtils for checking compatibility of autoboxed params
One more variant is using very powerfull JOOR library https://github.com/jOOQ/jOOR
MyObject myObject = new MyObject()
on(myObject).get("privateField");
It allows to modify any fields like final static constants and call yne protected methods without specifying concrete class in the inheritance hierarhy
<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joor-java-8</artifactId>
<version>0.9.7</version>
</dependency>

Categories

Resources