Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In our previous article, we discussed how we could read the values of private fields from a different class in Java. However, there can be scenarios when we need to set the values of fields, such as in some libraries where we don't have access to the fields.

In this quick tutorial, we'll discuss how can we set the values of fields from a different class in Java by using the Reflection API.

Note that we'll be using the same Person class for the examples here as we used in our previous article.

2. Setting Primitive Fields

We can set the fields that are primitives by using the Field#setXxx methods.

2.1. Setting Integer Fields

We can use the setByte, setShort, setInt, and setLong methods to set the byte, short, int, and long fields, respectively:

@Test
public void whenSetIntegerFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field ageField = person.getClass()
        .getDeclaredField("age");
    ageField.setAccessible(true);

    byte age = 26;
    ageField.setByte(person, age);
    Assertions.assertEquals(age, person.getAge());

    Field uidNumberField = person.getClass()
        .getDeclaredField("uidNumber");
    uidNumberField.setAccessible(true);

    short uidNumber = 5555;
    uidNumberField.setShort(person, uidNumber);
    Assertions.assertEquals(uidNumber, person.getUidNumber());

    Field pinCodeField = person.getClass()
        .getDeclaredField("pinCode");
    pinCodeField.setAccessible(true);

    int pinCode = 411057;
    pinCodeField.setInt(person, pinCode);
    Assertions.assertEquals(pinCode, person.getPinCode());

    Field contactNumberField = person.getClass()
        .getDeclaredField("contactNumber");
    contactNumberField.setAccessible(true);

    long contactNumber = 123456789L;
    contactNumberField.setLong(person, contactNumber);
    Assertions.assertEquals(contactNumber, person.getContactNumber());

}

It's also possible to perform unboxing with primitive types:

@Test
public void whenDoUnboxing_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field pinCodeField = person.getClass()
        .getDeclaredField("pinCode");
    pinCodeField.setAccessible(true);

    Integer pinCode = 411057;
    pinCodeField.setInt(person, pinCode);
    Assertions.assertEquals(pinCode, person.getPinCode());
}

The setXxx methods for primitive data types also support narrowing:

@Test
public void whenDoNarrowing_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field pinCodeField = person.getClass()
        .getDeclaredField("pinCode");
    pinCodeField.setAccessible(true);

    short pinCode = 4110;
    pinCodeField.setInt(person, pinCode);
    Assertions.assertEquals(pinCode, person.getPinCode());
}

2.2. Setting Floating Type Fields

To set float and double fields, we need to use the setFloat and setDouble methods, respectively:

@Test
public void whenSetFloatingTypeFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field heightField = person.getClass()
        .getDeclaredField("height");
    heightField.setAccessible(true);

    float height = 6.1242f;
    heightField.setFloat(person, height);
    Assertions.assertEquals(height, person.getHeight());

    Field weightField = person.getClass()
        .getDeclaredField("weight");
    weightField.setAccessible(true);

    double weight = 75.2564;
    weightField.setDouble(person, weight);
    Assertions.assertEquals(weight, person.getWeight());
}

2.3. Setting Character Fields

To set the char fields, we can use the setChar method:

@Test
public void whenSetCharacterFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field genderField = person.getClass()
        .getDeclaredField("gender");
    genderField.setAccessible(true);

    char gender = 'M';
    genderField.setChar(person, gender);
    Assertions.assertEquals(gender, person.getGender());
}

2.4. Setting Boolean Fields

Similarly, we can use the setBoolean method to set the boolean field:

@Test
public void whenSetBooleanFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field activeField = person.getClass()
        .getDeclaredField("active");
    activeField.setAccessible(true);

    activeField.setBoolean(person, true);
    Assertions.assertTrue(person.isActive());
}

3. Setting Fields That Are Objects

We can set the fields that are objects by using the Field#set method:

@Test
public void whenSetObjectFields_thenSuccess() 
  throws Exception {
    Person person = new Person();

    Field nameField = person.getClass()
        .getDeclaredField("name");
    nameField.setAccessible(true);

    String name = "Umang Budhwar";
    nameField.set(person, name);
    Assertions.assertEquals(name, person.getName());
}

4. Exceptions

Now, let's discuss the exceptions that the JVM can throw while setting the fields.

4.1. IllegalArgumentException

The JVM will throw IllegalArgumentException if we use a setXxx mutator that is incompatible with the target field's type. In our example, if we write nameField.setInt(person, 26), the JVM throws this exception since the field is of type String and not int or Integer:

@Test
public void givenInt_whenSetStringField_thenIllegalArgumentException() 
  throws Exception {
    Person person = new Person();
    Field nameField = person.getClass()
        .getDeclaredField("name");
    nameField.setAccessible(true);

    Assertions.assertThrows(IllegalArgumentException.class, () -> nameField.setInt(person, 26));
}

As we've already seen, the setXxx methods support narrowing for the primitive types. It's important to note that we need to provide the correct target for narrowing to be successful. Otherwise, the JVM throws an IllegalArgumentException:

@Test
public void givenInt_whenSetLongField_thenIllegalArgumentException() 
  throws Exception {
    Person person = new Person();

    Field pinCodeField = person.getClass()
        .getDeclaredField("pinCode");
    pinCodeField.setAccessible(true);

    long pinCode = 411057L;

    Assertions.assertThrows(IllegalArgumentException.class, () -> pinCodeField.setLong(person, pinCode));
}

4.2. IllegalAccessException

If we're trying to set a private field that doesn't have access rights, then the JVM will throw an IllegalAccessException. In the above example, if we don't write the statement nameField.setAccessible(true), then the JVM throws the exception:

@Test
public void whenFieldNotSetAccessible_thenIllegalAccessException() 
  throws Exception {
    Person person = new Person();
    Field nameField = person.getClass()
        .getDeclaredField("name");

    Assertions.assertThrows(IllegalAccessException.class, () -> nameField.set(person, "Umang Budhwar"));
}

5. Conclusion

In this tutorial, we've seen how we can modify or set the values of private fields of a class from another class in Java. We've also seen the exceptions that the JVM can throw and what causes them.

As always, the complete code for this example is available over on GitHub.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!