JavaRush /Blog Java /Random-FR /Pause café #230. Que sont les enregistrements en Java et ...

Pause café #230. Que sont les enregistrements en Java et comment fonctionnent-ils ?

Publié dans le groupe Random-FR
Source : JavaTechOnline Cet article fournit un examen approfondi du concept d'enregistrements en Java avec des exemples, y compris leur syntaxe, comment les créer et comment les utiliser. Pause café #230.  Que sont les enregistrements en Java et comment fonctionnent-ils - 1Les enregistrements en Java sont l'une des fonctionnalités intéressantes qui ont été introduites pour la première fois dans Java 14 en tant que fonctionnalité d'aperçu et ont finalement été publiées dans la version Java 17. De nombreux développeurs l'utilisent activement, ce qui les aide à réduire avec succès une énorme quantité de code passe-partout. De plus, grâce aux enregistrements, vous n'avez pas besoin d'écrire une seule ligne de code pour rendre une classe immuable.

Quand utiliser Record en Java ?

Si vous souhaitez transmettre des données immuables entre différentes couches de votre application, utiliser Record peut être un bon choix. Par défaut, les enregistrements en Java sont immuables, ce qui signifie que nous ne pouvons pas modifier leurs propriétés une fois créés. En conséquence, cela nous aide à éviter les erreurs et améliore la fiabilité du code. En termes simples, en utilisant Record en Java, nous pouvons générer automatiquement des classes.

Où puis-je utiliser Record en Java ?

Généralement, nous pouvons utiliser des enregistrements dans toutes les situations où nous devons déclarer des conteneurs de données simples avec des propriétés immuables et des méthodes générées automatiquement. Par exemple, voici quelques cas d'utilisation dans lesquels les enregistrements peuvent être utiles :  Objets de transfert de données (DTO) : nous pouvons utiliser Record pour déclarer des objets de transfert de données simples contenant des données. Ceci est utile lors du transfert de données entre différentes couches d'application, par exemple entre la couche de service et la couche de base de données. Objets de configuration : l'enregistrement peut être utilisé pour déclarer des objets de configuration qui contiennent un ensemble de propriétés de configuration pour une application ou un module. Ces objets ont généralement des propriétés immuables, ce qui les rend thread-safe et faciles à utiliser. Objets de valeur. Record peut être utilisé pour déclarer des objets de valeur contenant un ensemble de valeurs qui représentent un concept ou un modèle de domaine spécifique. Réponses de l'API : lors de la création d'une API REST, les données sont généralement renvoyées sous forme de JSON ou XML. Dans de tels cas, vous devrez peut-être définir une structure de données simple qui représente la réponse de l'API. Les enregistrements sont idéaux pour cela car ils vous permettent de définir une structure de données légère et immuable qui peut être facilement sérialisée au format JSON ou XML. Données de test. Lors de l'écriture de tests unitaires, vous devez souvent créer des données de test qui représentent un scénario spécifique. Dans de tels cas, vous devrez peut-être définir une structure de données simple qui représente les données de test. Les enregistrements peuvent être idéaux pour cela car ils nous permettent de définir une structure de données légère et immuable avec un code passe-partout minimal. Objets de type Tuple : les enregistrements peuvent être utilisés pour déclarer des objets de type Tuple qui contiennent un nombre fixe de valeurs associées. Cela peut être utile lors du renvoi de plusieurs valeurs d'une méthode ou lorsque vous travaillez avec des collections de valeurs associées.

Qu’est-ce que l’enregistrement en Java ?

Record en Java est une classe conçue pour stocker des données. Elle est similaire à une classe Java traditionnelle, mais elle est plus légère et possède des fonctionnalités uniques. Les enregistrements sont immuables par défaut, ce qui signifie que leur état ne peut pas être modifié une fois créés. Cela les rend idéaux pour stocker des données immuables, telles que des paramètres de configuration ou des valeurs renvoyées par une requête de base de données. C'est également un moyen de créer un type de données personnalisé composé d'un ensemble de champs ou de variables, ainsi que de méthodes pour accéder et modifier ces champs. Record en Java facilite l'utilisation des données en réduisant la quantité de code passe-partout que les développeurs doivent écrire encore et encore. La syntaxe de création d'une entrée en Java est la suivante :
record Record_Name(Fields....)

Comment créer et utiliser Record en Java avec des exemples ?

Voyons comment créer et utiliser un enregistrement en Java par programme.

Créer un enregistrement

La création d'un enregistrement par programmation n'est pas très similaire à la création d'une classe normale en Java. Au lieu de class , nous utilisons le mot-clé record . Vous devez également déclarer les champs avec des types de données entre parenthèses du nom de l'enregistrement. Voici un exemple de code qui montre comment créer une entrée en Java :
public record Book(String name, double price) { }
Dans cet exemple, nous avons créé un enregistrement appelé Book avec deux champs : name et price . Le mot-clé public indique que cette entrée est accessible en dehors du package dans lequel elle est définie.

Utiliser l'enregistrement

Pour utiliser un enregistrement en Java, nous pouvons l'instancier à l'aide du mot-clé new, comme nous le faisons avec une classe normale. Voici un exemple :
Book book = new Book( "Core Java" , 324.25);
Ici, une nouvelle instance d'enregistrement Book est créée avec le champ name défini sur Core Java et le champ price défini sur 324.25 . Une fois qu'un enregistrement est créé, ses champs sont accessibles en utilisant le nom du champ lui-même. Il n’existe pas de méthodes get ou set. Au lieu de cela, le nom du champ devient le nom de la méthode.
String name = book.name();

double price = book.price();
Ici, nous voyons la valeur des champs name et price extraits de l' enregistrement Book . Outre les champs, les enregistrements disposent également de méthodes intégrées que vous pouvez utiliser pour les manipuler. Par exemple, toString() , equals() et hashCode() . N'oubliez pas que les enregistrements Java sont immuables par défaut, ce qui signifie qu'une fois créés, leur état ne peut pas être modifié. Examinons un autre exemple de création et d'utilisation d'enregistrements en Java. Prenons un cas où nous avons besoin d'un enregistrement pour stocker des informations sur un étudiant.
public record Student(String name, int age, String subject) {}
Cela crée un enregistrement appelé Student avec trois champs : name , age et subject . Nous pouvons créer une instance de cette entrée comme suit :
Student student = new Student("John Smith", 20, "Computer Science");
On peut alors récupérer les valeurs du champ en utilisant le nom du champ :
String name = student.name();
int age = student.age();
String subject= student.subject();

A quoi ressemble le disque après compilation ?

Puisque Record n'est qu'un type spécial de classe, le compilateur la convertit également en une classe normale, mais avec quelques restrictions et différences. Lorsque le compilateur convertit un enregistrement (fichier Java) en bytecode après le processus de compilation, le fichier .class généré contient quelques déclarations supplémentaires de la classe Record . Par exemple, vous trouverez ci-dessous le bytecode généré pour l' entrée Student par le compilateur Java :
record Student(String name, int age) {   }

Écriture dans un fichier .class (après compilation)

Nous pouvons également trouver la classe convertie ci-dessous si nous utilisons l' outil javap et appliquons la commande ci-dessous à partir de la ligne de commande. Il est important de noter que la ligne de commande Java inclut l' outil javap , que vous pouvez utiliser pour afficher des informations sur les champs, les constructeurs et les méthodes d'un fichier de classe. >javap Conclusion de l'étudiant : Si nous vérifions attentivement le bytecode, nous pouvons avoir quelques observations :
  • Le compilateur a remplacé le mot-clé Record par class .
  • Le compilateur a déclaré la classe final . Cela indique que cette classe ne peut pas être étendue. Cela signifie également qu’il ne peut pas être hérité et qu’il est de nature immuable.
  • La classe convertie étend java.lang.Record . Cela indique que tous les enregistrements sont une sous-classe de la classe Record définie dans le package java.lang .
  • Le compilateur ajoute un constructeur paramétré.
  • Le compilateur a généré automatiquement les méthodes toString() , hashCode() et equals() .
  • Le compilateur a ajouté des méthodes pour accéder aux champs. Faites attention à la convention de dénomination des méthodes - elles sont exactement les mêmes que les noms de champs, il ne doit pas y avoir de get ou de set avant les noms de champs .

Champs dans les enregistrements

Les enregistrements en Java définissent leur état à l'aide d'un ensemble de champs, chacun avec un nom et un type différents. Les champs d'un enregistrement sont déclarés dans l'entête de l'enregistrement. Par exemple:
public record Person(String name, int age) {}
Cette entrée comporte deux champs : nom du type String et âge du type int . Les champs d'un enregistrement sont implicitement définitifs et ne peuvent pas être réaffectés après la création de l'enregistrement. Nous pouvons ajouter un nouveau champ, mais ce n'est pas recommandé. Un nouveau champ ajouté à un enregistrement doit être statique. Par exemple:
public record Person(String name, int age) {

   static String sex;
}

Constructeurs dans les enregistrements

Comme les constructeurs de classes traditionnels, les constructeurs d'enregistrements sont utilisés pour créer des instances d'enregistrement. Records a deux concepts de constructeurs : le constructeur canonique et le constructeur compact . Le constructeur compact offre une manière plus concise d'initialiser les variables d'état dans un enregistrement, tandis que le constructeur canonique offre une manière plus traditionnelle avec plus de flexibilité.

Constructeur canonique

Le compilateur Java par défaut nous fournit un constructeur tous arguments (constructeur tous champs) qui attribue ses arguments aux champs correspondants. Il est connu sous le nom de constructeur canonique. Nous pouvons également ajouter une logique métier telle que des instructions conditionnelles pour valider les données. Ci-dessous un exemple :
public record Person(String name, int age) {

       public Person(String name, int age) {
           if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
           }
      }
}

Concepteur compact

Les constructeurs compacts ignorent tous les arguments, y compris les parenthèses. Les champs correspondants sont attribués automatiquement. Par exemple, le code suivant illustre le concept d'un constructeur compact dans Records :
public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
En plus du constructeur compact, vous pouvez définir des constructeurs réguliers dans Record , tout comme dans une classe normale. Cependant, vous devez vous assurer que tous les constructeurs initialisent tous les champs de l'enregistrement.

Méthodes dans les enregistrements

Les enregistrements en Java génèrent automatiquement un ensemble de méthodes pour chaque champ de l'enregistrement. Ces méthodes sont appelées méthodes accesseurs et portent le même nom que le champ auquel elles sont associées. Par exemple, si un enregistrement a un champ nommé price , alors il aura automatiquement une méthode appelée price() qui renvoie la valeur du champ price . En plus des méthodes d'accesseur générées automatiquement, nous pouvons également définir nos propres méthodes dans une entrée, tout comme dans une classe normale. Par exemple:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
Cette entrée a une méthode sayHello() qui imprime un message d'accueil en utilisant le champ name .

Comment Record aide à réduire le code passe-partout

Examinons un exemple simple pour voir comment les notations Java peuvent aider à éliminer le code passe-partout. Disons que nous avons une classe appelée Person qui représente une personne avec son nom, son âge et son adresse e-mail. C’est ainsi que nous définissons une classe de manière traditionnelle. Coder sans utiliser Record :
public class Person {

    private String name;
    private int age;
    private String email;

    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {

       return name;
    }

    public int getAge() {
       return age;
    }

    publicString getEmail() {
       return email;
    }

    public void setName(String name) {
       this.name = name;
    }

    public voidsetAge(int age) {
       this.age = age;
    }

    public void setEmail(String email) {
       this.email = email;
    }

    @Override
    public String toString() {
       return "Person{" +
         "name='" + name + '\'' +
         ", age=" + age +
         ", email='" + email + '\'' +
      '}';
    }
}
Comme nous pouvons le voir, cette classe nécessite beaucoup de code passe-partout pour définir les champs, le constructeur, les getters, les setters et la méthode toString() . De plus, supposons que nous souhaitions rendre cette classe immuable. Pour ce faire, nous devrons effectuer quelques étapes supplémentaires telles que :
  • Déclarez la classe comme finale afin qu'elle ne puisse pas être étendue.
  • Déclarez tous les champs private et final afin qu'ils ne puissent pas être modifiés en dehors du constructeur.
  • Ne fournissez aucune méthode de définition pour les champs.
  • Si l'un des champs est modifiable, vous devez en renvoyer une copie au lieu de renvoyer l'objet d'origine.
Coder en utilisant Record :
public record Person(String name, int age, String email) {}
C'est tout! Avec une seule ligne de code, nous avons défini une classe qui a les mêmes champs, constructeur, getters, setters et méthode toString() qu'une classe traditionnelle. La syntaxe Record s’occupe de tout le code passe-partout pour nous. D'après l'exemple ci-dessus, il est clair que l'utilisation d'enregistrements en Java peut aider à éliminer le code passe-partout en fournissant une syntaxe plus concise pour définir des classes avec un ensemble fixe de champs. Moins de code est nécessaire pour définir et utiliser les enregistrements que l'approche traditionnelle, et les enregistrements fournissent un accès direct aux champs à l'aide de méthodes pour mettre à jour les champs. Cela rend le code plus lisible, maintenable et moins sujet aux erreurs. De plus, avec la syntaxe Record , nous n'avons rien de plus à faire pour rendre la classe immuable. Par défaut, tous les champs d'un enregistrement sont définitifs et la classe d'enregistrement elle-même est immuable. Par conséquent, créer une classe immuable à l’aide de la syntaxe d’écriture est beaucoup plus simple et nécessite moins de code que l’approche traditionnelle. Avec les enregistrements, nous n'avons pas besoin de déclarer les champs comme final , de fournir un constructeur qui initialise les champs ou de fournir des getters pour tous les champs.

Classes d'enregistrement communes

Java peut également définir des classes Records génériques . Une classe d'entrée générique est une classe d'entrée qui possède un ou plusieurs paramètres de type. Voici un exemple:
public record Pair<T, U>(T first, U second) {}
Dans cet exemple , Pair est une classe d'enregistrement générique qui prend deux paramètres de type T et U. Nous pouvons créer une instance de cet enregistrement comme suit :
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Classe imbriquée dans Record

Vous pouvez également définir des classes et des interfaces imbriquées dans une entrée. Ceci est utile pour regrouper les classes et interfaces associées et peut aider à améliorer l’organisation et la maintenabilité de votre base de code. Voici un exemple d'entrée contenant une classe imbriquée :
public record Person(String name, int age, Contact contact){

    public static class Contact {

       private final String email;
       private final String phone;

       public Contact(String email, String phone){
           this.email = email;
           this.phone = phone;
       }

       public String getEmail(){
          return email;
       }

       public String getPhone(){
          return phone;
       }
    }
}
Dans cet exemple, Person est une entrée contenant une classe Contact imbriquée . À son tour, Contact est une classe imbriquée statique qui contient deux champs finaux privés : l'adresse e-mail et le téléphone. Il dispose également d'un constructeur qui accepte un e-mail et un numéro de téléphone, ainsi que de deux méthodes getter : getEmail() et getPhone() . Nous pouvons créer une instance Person comme ceci :
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
Dans cet exemple, nous avons créé un nouvel objet Person nommé John , âgé de 30 ans, et un nouvel objet Contact avec l'e-mail john@example.com et le téléphone 123-456-7890 .

Interface imbriquée dans Record

Voici un exemple d'entrée contenant une interface imbriquée :
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
Dans cet exemple, Book est l'entrée contenant l' interface Edition imbriquée . À son tour, Edition est une interface qui définit une seule méthode getName() . Nous pouvons créer une instance Book comme suit :
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
Dans cet exemple, nous créons un nouvel objet Book avec le titre The Hitchhiker's Guide to the Galaxy de Douglas Adams et une nouvelle implémentation anonyme de l'interface Edition qui renvoie le nom Science Fiction lorsque la méthode getName() est appelée .

Que peuvent faire d'autre les enregistrements ?

  • Les entrées peuvent définir des constructeurs personnalisés. Les enregistrements prennent en charge les constructeurs paramétrés, qui peuvent appeler un constructeur par défaut avec les paramètres fournis dans leur corps. De plus, les enregistrements prennent également en charge les constructeurs compacts, qui sont similaires aux constructeurs par défaut mais peuvent inclure des fonctionnalités supplémentaires telles que des vérifications dans le corps du constructeur.
  • Comme toute autre classe Java, Record peut définir et utiliser des méthodes d’instance. Cela signifie que nous pouvons créer et appeler des méthodes spécifiques à la classe d'enregistrement.
  • Dans Record , la définition de variables d'instance en tant que membres de classe n'est pas autorisée car elles ne peuvent être spécifiées qu'en tant que paramètres de constructeur. Toutefois, les enregistrements prennent en charge les champs statiques et les méthodes statiques, qui peuvent être utilisées pour stocker et accéder aux données communes à toutes les instances de la classe d'enregistrement.

Un enregistrement peut-il implémenter des interfaces ?

Oui, écrire en Java peut implémenter des interfaces. Par exemple, le code ci-dessous illustre le concept :
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
Ici, nous avons défini une interface imprimable avec une seule méthode print() . Nous avons également défini une entrée Person qui implémente l' interface Printable . L' enregistrement Personne comporte deux champs : nom et âge , et remplace la méthode d'impression de l'interface imprimable pour imprimer les valeurs de ces champs. Nous pouvons instancier une entrée Personne et appeler sa méthode d'impression comme ceci :
Person person = new Person("John", 30);
person.print();
Cela affichera Name: John, Age: 30 sur la console . Comme le montre l'exemple, les enregistrements Java peuvent implémenter des interfaces tout comme les classes classiques. Cela peut être utile pour ajouter un comportement à une entrée ou pour garantir qu'une entrée est conforme à un contrat défini par une interface.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION