1. Обзор
В этом кратком руководстве мы обсудим, как мы можем получить доступ к значению поля private из другого класса в Java.
Прежде чем приступить к учебнику, мы должны понять, что модификатор private access предотвращает случайное неправильное использование полей. Однако, если мы хотим получить к ним доступ, мы можем сделать это с помощью API Reflection .
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);
В приведенном выше примере мы сначала указываем поле, которое мы хотим получить – name – с помощью метода Class#getDeclaredField . Затем мы делаем поле доступным с помощью name Field.setAccessible(true) .
4. Доступ к закрытым примитивным полям
Мы можем получить доступ к полям private , которые являются примитивами, используя методы Field#getXXX .
4.1. Доступ к целочисленным полям
Мы можем использовать методы getByte, getShort , getInt и get Long для доступа к полям 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 для доступа к полю boolean :
@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. Доступ К закрытым Полям, Которые Являются Объектами
Мы можем получить доступ к полям private , которые являются объектами, с помощью метода Field#get |. Следует отметить , что универсальный get метод возвращает Объект , поэтому нам нужно будет привести его к целевому типу, чтобы использовать значение :
@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 может создавать при доступе к полям private .
6.1. Исключение незаконных аргументов
JVM бросит IllegalArgumentException если мы используем метод доступа getXXX , несовместимый с типом целевого поля . В нашем примере, если мы напишем name Field.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. Исключение IllegalAccessException
JVM выдаст исключение IllegalAccessException , если мы пытаемся получить доступ к полю, у которого нет прав доступа . В приведенном выше примере, если мы не напишем оператор name Field.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. Исключение NoSuchFieldException
Если мы попытаемся получить доступ к полю, которое не существует в классе Person , то JVM может выдать NoSuchFieldException :
Assertions.assertThrows(NoSuchFieldException.class, () -> person.getClass().getDeclaredField("firstName"));
6.4. Исключение NullPointerException
Наконец, как и следовало ожидать, JVM выдает исключение NullPointerException , если мы передаем имя поля как null :
Assertions.assertThrows(NullPointerException.class, () -> person.getClass().getDeclaredField(null));
7. Заключение
В этом уроке мы рассмотрели, как мы можем получить доступ к полям private класса в другом классе. Мы также видели исключения, которые может создавать JVM, и то, что их вызывает.
Как всегда, полный код для этого примера доступен на GitHub .