1. 概要

以前の記事では、Javaの別のクラスからprivateフィールドの値を読み取る方法について説明しました。 ただし、フィールドにアクセスできない一部のライブラリなど、フィールドの値を設定する必要があるシナリオが存在する可能性があります。

このクイックチュートリアルでは、 Reflection APIを使用して、Javaの異なるクラスのフィールドの値を設定する方法について説明します。

ここでの例では、前の記事で使用したものと同じ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());

}

プリミティブ型を使用して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());
}

プリミティブデータ型のsetXxx メソッドは、ナローイングもサポートします。

@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 メソッドを使用して、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. オブジェクトであるフィールドの設定

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

ターゲットフィールドのタイプと互換性のないsetXxxミューテーターを使用すると、JVMは IllegalArgumentExceptionをスローします。 この例では、 nameField.setInt(person、26)と書くと、フィールドのタイプが String であり、 int ではないため、JVMはこの例外をスローします。 整数

@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. IllegalAccessException

アクセス権を持たないプライベートフィールドを設定しようとすると、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から入手できます。