Lecture de la valeur des champs `` privés '' d'une classe différente en Java

1. Vue d'ensemble

Dans ce rapide didacticiel, nous verrons comment accéder à la valeur d'un champ privé à partir d'une classe différente en Java.

Avant de commencer le didacticiel, nous devons comprendre que le modificateur d'accès privé empêche l'utilisation abusive accidentelle des champs. Cependant, si nous souhaitons y accéder, nous pouvons le faire en utilisant l'API Reflection.

2. Exemple

Définissons un exemple de classe Person avec des champs privés :

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. Rendre les champs privés accessibles

Pour rendre un champ privé accessible, nous devons appeler la méthode Field # setAccessible :

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

Dans l'exemple ci-dessus, nous spécifions d' abord le champ que nous voulons récupérer - nom - en utilisant la méthode Class # getDeclaredField . Ensuite, nous rendons le champ accessible en utilisant nameField.setAccessible (true) .

4. Accès aux champs primitifs privés

Nous pouvons accéder aux champs privés qui sont des primitives en utilisant les méthodes Field # getXxx .

4.1. Accès aux champs entiers

Nous pouvons utiliser les méthodes getByte, getShort , getInt et getLong pour accéder respectivement aux champs byte , short , int et 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); }

Il est également possible d'effectuer une autobox avec des types primitifs:

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

Les méthodes getXxx pour les types de données primitifs prennent également en charge l'élargissement:

@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. Accès aux champs de type flottant

Pour accéder aux champs float et double , nous devons utiliser les méthodes getFloat et getDouble , respectivement:

@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. Accéder aux champs de caractères

Pour accéder aux champs char , nous pouvons utiliser la méthode 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. Accès aux champs booléens

De même, nous pouvons utiliser la méthode getBoolean pour accéder au champ booléen :

@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. Accès aux champs privés qui sont des objets

Nous pouvons accéder aux champs privés qui sont des objets en utilisant la méthode Field # get . Il est à noter que la méthode get générique renvoie un Object , nous devrons donc le convertir en type cible pour utiliser la valeur :

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

Maintenant, discutons des exceptions que la JVM peut lever lors de l'accès aux champs privés .

6.1. Exception d'argument illégal

La JVM lèvera IllegalArgumentException si nous utilisons un accesseur getXxx incompatible avec le type du champ cible . Dans notre exemple, si nous écrivons nameField.getInt (person) , la JVM lève cette exception car le champ est de type String et non int ou 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)); }

Comme nous l'avons déjà vu, les méthodes getXxx prennent en charge l'élargissement pour les types primitifs. Il est important de noter que nous devons fournir la cible correcte pour que l'élargissement réussisse . Sinon, la JVM lève une exception 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

La machine virtuelle Java lèvera une exception IllegalAccessException si nous essayons d'accéder à un champ qui n'a pas de droits d'accès . Dans l'exemple ci-dessus, si nous n'écrivons pas l'instruction nameField.setAccessible (true) , la JVM lève l'exception:

@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

Si nous essayons d'accéder à un champ qui n'existe pas dans la classe Person , la JVM pourrait lancer NoSuchFieldException :

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

6.4. NullPointerException

Enfin, comme vous vous en doutez, la JVM lève une NullPointerException si nous transmettons le nom du champ à null :

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

7. Conclusion

Dans ce didacticiel, nous avons vu comment accéder aux champs privés d'une classe dans une autre classe. Nous avons également vu les exceptions que la JVM peut lever et ce qui les provoque.

Comme toujours, le code complet de cet exemple est disponible à l'adresse over sur GitHub.