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 .