Перейти к основному содержимому

Установить значение поля с отражением

· 4 мин. чтения

1. Обзор

В нашей предыдущей статье мы обсуждали, как мы можем читать значения приватных полей из другого класса в Java. Однако могут быть сценарии, когда нам нужно установить значения полей, например, в некоторых библиотеках, где у нас нет доступа к полям.

В этом кратком руководстве мы обсудим, как мы можем установить значения полей из другого класса в Java с помощью API Reflection .

Обратите внимание, что мы будем использовать в примерах тот же класс Person , что и в предыдущей статье .

2. Настройка примитивных полей

Мы можем установить поля, которые являются примитивами, используя методы Field#setXxx .

2.1. Настройка целочисленных полей

Мы можем использовать методы setByte, setShort , s etInt и 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());

}

Также возможно выполнить распаковку с примитивными типами:

@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());
}

Методы s etXxx для примитивных типов данных также поддерживают сужение :

@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. Настройка полей плавающего типа

Чтобы установить поля float и double , нам нужно использовать методы setFloat и setDouble соответственно:

@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. Настройка полей символов

Чтобы установить поля 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. Настройка логических полей

Точно так же мы можем использовать метод setBoolean для установки логического поля:

@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. Настройка полей, которые являются объектами

Мы можем установить поля, которые являются объектами, используя метод 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. Исключения

Теперь давайте обсудим исключения, которые JVM может генерировать при установке полей.

4.1. IllegalArgumentException

JVM выдаст исключение IllegalArgumentException , если мы используем мутатор setXxx , несовместимый с типом целевого поля . В нашем примере, если мы напишем nameField.setInt(person, 26) , JVM выдаст это исключение, поскольку поле имеет тип String , а не int или 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));
}

Как мы уже видели, методы s etXxx поддерживают сужение примитивных типов. Важно отметить, что нам нужно указать правильную цель для успешного сужения . В противном случае 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. Нелегальное исключение доступа

Если мы пытаемся установить приватное поле, у которого нет прав доступа , то 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. Вывод

В этом руководстве мы увидели, как мы можем изменить или установить значения частных полей класса из другого класса в Java. Мы также видели исключения, которые может генерировать JVM, и их причины.

Как всегда, полный код этого примера доступен на GitHub .