Set Field Value With Reflection – 用反射设置字段值

最后修改: 2020年 8月 9日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

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.

在我们的前一篇文章中,我们讨论了如何从Java的不同类中读取private字段的值。然而,在有些情况下,我们需要设置字段的值,例如在一些库中,我们无法访问这些字段。

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.

在这个快速教程中,我们将讨论如何通过使用反射API来设置Java中不同类的字段的值。

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

注意,我们将在这里的示例中使用与我们在前一篇文章中使用的相同的Person

2. Setting Primitive Fields

2.设置原始字段

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

我们可以通过使用Field#setXxx方法设置属于基元的字段。

2.1. Setting Integer Fields

2.1.设置整数字段

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

我们可以使用setByte, setShort, setInt, 和setLong方法来分别设置byte, short, int, 和long字段。

@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:

也可以对原始类型执行unboxing

@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:

原始数据类型的setXxx方法也支持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

2.2.设置浮动类型字段

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

要设置floatdouble字段,我们需要分别使用setFloatsetDouble方法。

@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

2.3.设置字符字段

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

为了设置char字段,我们可以使用setChar方法。

@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

2.4.设置布尔字段

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

同样地,我们可以使用setBoolean方法来设置boolean字段。

@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

3.设置属于对象的字段

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

我们可以通过使用Field#set方法设置属于对象的字段。

@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

4.例外情况

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

现在,让我们讨论一下JVM在设置字段时可能会出现的异常。

4.1. IllegalArgumentException

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:

JVM将抛出IllegalArgumentException 如果我们使用的setXxx突变器与目标字段的类型不兼容。在我们的例子中,如果我们写nameField.person, 26),JVM就会抛出这个异常,因为该字段的类型是String,而不是intInteger

@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:

正如我们已经看到的,setXxx方法支持对原始类型进行缩小。值得注意的是,我们需要提供正确的目标,才能成功缩小范围。否则,JVM会抛出一个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

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:

如果我们试图设置一个没有访问权限的private字段,那么JVM将抛出一个IllegalAccessException。在上面的例子中,如果我们不写语句nameField.setAccessible(true),那么JVM会抛出这个异常。

@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

5.总结

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.

在本教程中,我们看到了如何从Java的另一个类中修改或设置一个类的私有字段的值。我们还看到了JVM可以抛出的异常以及导致这些异常的原因。

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

一如既往,本例的完整代码可在GitHub上获得over