Close

Java - Different ways to Set Nested Field Value By Reflection

[Updated: Apr 3, 2020, Created: Apr 3, 2020]

Java Reflection Java 

This tutorial shows different ways to set nest field values by using Java Reflection.

Examples

Example POJOs

package com.logicbig.example;

public class Person {
  private Address address;
    .............
}
package com.logicbig.example;

public class Address {
  private AddressLine line1;
  private City city;
    .............
}
package com.logicbig.example;

public class AddressLine {
  private String houseNumber;
  private String street;
    .............
}
package com.logicbig.example;

public class City {
  private String cityName;
    .............
}

Using java.lang.reflect.Field

package com.logicbig.example;

import java.lang.reflect.Field;

public class JavaFieldExample {
  public static void main(String[] args) throws Exception {
      Person p = new Person();
      Address address = new Address();
      address.setLine1(new AddressLine());
      address.setCity(new City());
      p.setAddress(address); //commenting this will also work
      System.out.println("before: " + p);
      setField(p, "address.line1.houseNumber", "4508");
      setField(p, "address.line1.street", "Westfall Dr");
      setField(p, "address.city.cityName", "Los Alamos");
      System.out.println("after: " + p);
  }

  private static void setField(Object object, String fieldName, Object fieldValue)
          throws Exception {
      if (fieldName.contains(".")) {
          int firstDotLocation = fieldName.indexOf('.');
          String childFieldName = fieldName.substring(0, firstDotLocation);
          Field field = object.getClass().getDeclaredField(childFieldName);
          field.setAccessible(true);
          Object childFieldInstance = field.get(object);
          if (childFieldInstance == null) {
              Class<?> type = field.getType();
              //invoking no argument constructor
              childFieldInstance = type.getConstructor().newInstance();
              field.set(object, childFieldInstance);
          }
          field.setAccessible(false);
          setField(childFieldInstance, fieldName.substring(firstDotLocation + 1), fieldValue);
      } else {
          Field field = object.getClass().getDeclaredField(fieldName);
          field.setAccessible(true);
          field.set(object, fieldValue);
          field.setAccessible(false);
      }
  }
}
before: Person{address=Address{line1=AddressLine{houseNumber='null', street='null'}, city=City{cityName='null'}}}
after: Person{address=Address{line1=AddressLine{houseNumber='4508', street='Westfall Dr'}, city=City{cityName='Los Alamos'}}}

Invoking setter via java.lang.reflect.Method

package com.logicbig.example;

import java.lang.reflect.Method;

public class JavaSetterMethodExample {
  public static void main(String[] args) throws Exception {
      Person p = new Person();//must have getters/setters
      Address address = new Address();
      address.setLine1(new AddressLine());
      address.setCity(new City());
      p.setAddress(address); //commenting this will also work
      System.out.println("before: " + p);
      setFieldValue(p, "address.line1.houseNumber", "4508");
      setFieldValue(p, "address.line1.street", "Westfall Dr");
      setFieldValue(p, "address.city.cityName", "Los Alamos");
      System.out.println("after: " + p);
  }

  private static void setFieldValue(Object object, String fieldName, Object fieldValue)
          throws Exception {
      if (fieldName.contains(".")) {
          int firstDotLocation = fieldName.indexOf('.');
          String childFieldName = fieldName.substring(0, firstDotLocation);
          Method getter = object.getClass().getDeclaredMethod(fieldToGetterName(childFieldName));
          Object childFieldInstance = getter.invoke(object);
          if (childFieldInstance == null) {
              Class<?> type = getter.getReturnType();
              //invoking no argument constructor
              childFieldInstance = type.getConstructor().newInstance();
              Method setter = object.getClass().getDeclaredMethod(
                      fieldToSetterName(childFieldName), type);
              setter.invoke(object, childFieldInstance);
          }
          setFieldValue(childFieldInstance, fieldName.substring(firstDotLocation + 1), fieldValue);
      } else {
          Method setter = object.getClass().getDeclaredMethod(fieldToSetterName(fieldName),
                  fieldValue.getClass());
          setter.invoke(object, fieldValue);
      }
  }

  private static String fieldToGetterName(String fieldName) {
      return "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
  }

  private static String fieldToSetterName(String fieldName) {
      return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
  }
}
before: Person{address=Address{line1=AddressLine{houseNumber='null', street='null'}, city=City{cityName='null'}}}
after: Person{address=Address{line1=AddressLine{houseNumber='4508', street='Westfall Dr'}, city=City{cityName='Los Alamos'}}}

Using BeanInfo

package com.logicbig.example;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Arrays;

public class JavaBeanInfoExample {
  public static void main(String[] args) throws Exception {
      Person p = new Person();//must have getters/setters
      Address address = new Address();
      address.setLine1(new AddressLine());
      address.setCity(new City());
      p.setAddress(address); //commenting this will also work
      System.out.println("before: " + p);
      setFieldValue(p, "address.line1.houseNumber", "4508");
      setFieldValue(p, "address.line1.street", "Westfall Dr");
      setFieldValue(p, "address.city.cityName", "Los Alamos");
      System.out.println("after: " + p);
  }

  private static void setFieldValue(Object object, String fieldName, Object fieldValue)
          throws Exception {
      if (fieldName.contains(".")) {
          int firstDotLocation = fieldName.indexOf('.');
          String childFieldName = fieldName.substring(0, firstDotLocation);
          PropertyDescriptor pd = findPropertyDescriptor(object.getClass(), childFieldName);
          Object childFieldInstance = pd.getReadMethod().invoke(object);

          if (childFieldInstance == null) {
              Class<?> type = pd.getPropertyType();
              childFieldInstance = type.getConstructor().newInstance();
              pd.getWriteMethod().invoke(object, childFieldInstance);
          }
          setFieldValue(childFieldInstance, fieldName.substring(firstDotLocation + 1), fieldValue);
      } else {
          PropertyDescriptor pd = findPropertyDescriptor(object.getClass(), fieldName);
          pd.getWriteMethod().invoke(object, fieldValue);
      }
  }

  private static PropertyDescriptor findPropertyDescriptor(Class<?> c, String fieldName)
          throws Exception {
      BeanInfo beanInfo = Introspector.getBeanInfo(c);
      return Arrays.stream(beanInfo.getPropertyDescriptors())
                   .filter(pd -> pd.getName().equals(fieldName))
                   .findAny()
                   .orElseThrow(() -> new IllegalArgumentException("field not found: " + fieldName));
  }
}
before: Person{address=Address{line1=AddressLine{houseNumber='null', street='null'}, city=City{cityName='null'}}}
after: Person{address=Address{line1=AddressLine{houseNumber='4508', street='Westfall Dr'}, city=City{cityName='Los Alamos'}}}

Using Apache Commons BeanUtils

pom.xml

<dependency>
   <groupId>commons-beanutils</groupId>
   <artifactId>commons-beanutils</artifactId>
   <version>1.9.4</version>
</dependency>
package com.logicbig.example;

import org.apache.commons.beanutils.BeanUtils;

public class ApacheCommonBeanUtilsExample {
  public static void main(String[] args) throws Exception {
      Person p = new Person();//must have getters and setters
      Address address = new Address();
      address.setLine1(new AddressLine());
      address.setCity(new City());
      p.setAddress(address);
      System.out.println("before: " + p);
      BeanUtils.setProperty(p, "address.line1.houseNumber", "4508");
      BeanUtils.setProperty(p, "address.line1.street", "Westfall Dr");
      BeanUtils.setProperty(p, "address.city.cityName", "Los Alamos");
      System.out.println("after: " + p);
  }
}
before: Person{address=Address{line1=AddressLine{houseNumber='null', street='null'}, city=City{cityName='null'}}}
after: Person{address=Address{line1=AddressLine{houseNumber='4508', street='Westfall Dr'}, city=City{cityName='Los Alamos'}}}

Using Spring beans

pom.xml

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-beans</artifactId>
   <version>5.2.5.RELEASE</version>
</dependency>

Direct Field Access

package com.logicbig.example;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.PropertyAccessorFactory;

public class SpringFieldAccessorExample {
  public static void main(String[] args) {
      Person p = new Person();
      Address address = new Address();
      address.setLine1(new AddressLine());
      address.setCity(new City());
      p.setAddress(address);
      System.out.println("before: " + p);
      ConfigurablePropertyAccessor propertyAccessor = PropertyAccessorFactory.forDirectFieldAccess(p);
      propertyAccessor.setPropertyValue("address.line1.houseNumber", "4508");
      propertyAccessor.setPropertyValue("address.line1.street", "Westfall Dr");
      propertyAccessor.setPropertyValue("address.city.cityName", "Los Alamos");
      System.out.println("after: " + p);
  }
}
before: Person{address=Address{line1=AddressLine{houseNumber='null', street='null'}, city=City{cityName='null'}}}
after: Person{address=Address{line1=AddressLine{houseNumber='4508', street='Westfall Dr'}, city=City{cityName='Los Alamos'}}}

Setter Access

package com.logicbig.example;

import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.PropertyAccessorFactory;

public class SpringPropertyAccessorExample {
  public static void main(String[] args) {
      Person p = new Person();//must have getters/setters
      Address address = new Address();
      address.setLine1(new AddressLine());
      address.setCity(new City());
      p.setAddress(address);
      System.out.println("before: " + p);
      ConfigurablePropertyAccessor propertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(p);
      propertyAccessor.setPropertyValue("address.line1.houseNumber", "4508");
      propertyAccessor.setPropertyValue("address.line1.street", "Westfall Dr");
      propertyAccessor.setPropertyValue("address.city.cityName", "Los Alamos");
      System.out.println("after: " + p);
  }
}
before: Person{address=Address{line1=AddressLine{houseNumber='null', street='null'}, city=City{cityName='null'}}}
after: Person{address=Address{line1=AddressLine{houseNumber='4508', street='Westfall Dr'}, city=City{cityName='Los Alamos'}}}

Example Project

Dependencies and Technologies Used:

  • commons-beanutils 1.9.4: Apache Commons BeanUtils provides an easy-to-use but flexible wrapper around reflection and introspection.
  • spring-beans 5.2.5.RELEASE: Spring Beans.
  • JDK 8
  • Maven 3.5.4

Java Setting Nested Field Value Select All Download
  • java-reflection-setting-nested-field-values
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ApacheCommonBeanUtilsExample.java

    See Also