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

Чтение значения «частных» полей из другого класса в Java

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

1. Обзор

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

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

2. Пример

Давайте определим пример класса Person с некоторыми приватными полями:

public class Person {

private String name = "John";
private byte age = 30;
private short uidNumber = 5555;
private int pinCode = 452002;
private long contactNumber = 123456789L;
private float height = 6.1242f;
private double weight = 75.2564;
private char gender = 'M';
private boolean active = true;

// getters and setters
}

3. Обеспечение доступа к приватным полям

Чтобы сделать любое приватное поле доступным, мы должны вызвать метод Field#setAccessible :

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

В приведенном выше примере мы сначала указываем поле, которое мы хотим получить — имя — с помощью метода Class#getDeclaredField . Затем мы делаем поле доступным, используя nameField.setAccessible(true) .

4. Доступ к частным примитивным полям

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

4.1. Доступ к целочисленным полям

Мы можем использовать методы getByte, getShort , getInt и getLong для доступа к полям byte , short , int и long соответственно:

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

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

byte age = ageField.getByte(person);
Assertions.assertEquals(30, age);

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

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

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

int pinCode = pinCodeField.getInt(person);
Assertions.assertEquals(452002, pinCode);

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

long contactNumber = contactNumberField.getLong(person);
Assertions.assertEquals(123456789L, contactNumber);
}

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

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

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

Integer pinCode = pinCodeField.getInt(person);
Assertions.assertEquals(452002, pinCode);
}

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

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

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

Long pinCode = pinCodeField.getLong(person);
Assertions.assertEquals(452002L, pinCode);
}

4.2. Доступ к полям с плавающим типом

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

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

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

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

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

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

4.3. Доступ к символьным полям

Чтобы получить доступ к полям char , мы можем использовать метод getChar :

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

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

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

4.4. Доступ к логическим полям

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

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

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

boolean active = activeField.getBoolean(person);
Assertions.assertTrue(active);
}

5. Доступ к частным полям, которые являются объектами

Мы можем получить доступ к закрытым полям, которые являются объектами, используя метод Field #get . Следует отметить, что общий метод get возвращает Object , поэтому нам нужно привести его к целевому типу, чтобы использовать значение :

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

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

String name = (String) nameField.get(person);
Assertions.assertEquals("John", name);
}

6. Исключения

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

6.1. IllegalArgumentException

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

Как мы уже видели, методы getXxx поддерживают расширение примитивных типов. Важно отметить, что для успешного расширения необходимо указать правильную цель . В противном случае JVM выдает исключение IllegalArgumentException :

@Test
public void givenInt_whenGetLongField_thenIllegalArgumentException()
throws Exception {
Person person = new Person();
Field contactNumberField = person.getClass().getDeclaredField("contactNumber");
contactNumberField.setAccessible(true);

Assertions.assertThrows(IllegalArgumentException.class, () -> contactNumberField.getInt(person));
}

6.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.get(person));
}

6.3. носачфиелдексцептион

Если мы попытаемся получить доступ к полю, которого нет в классе Person , то JVM может сгенерировать NoSuchFieldException :

Assertions.assertThrows(NoSuchFieldException.class,
() -> person.getClass().getDeclaredField("firstName"));

6.4. Исключение нулевого указателя

Наконец, как и следовало ожидать, JVM выдает исключение NullPointerException , если мы передаем имя поля как null :

Assertions.assertThrows(NullPointerException.class,
() -> person.getClass().getDeclaredField(null));

7. Заключение

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

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