Java 14: aperçu des enregistrements

Bientôt, une nouvelle fonctionnalité de syntaxe apparaîtra dans les prochains enregistrements Java 14 . Après avoir étudié l' aperçu , qui décrit brièvement à quoi ressemblent les enregistrements et avec «ce qu'ils mangent», j'ai osé adapter le document en russe pour Habr. Peu importe - bienvenue au chat.


Résumé


Les entrées vous permettent d'étendre les capacités de Java. Ils fournissent une syntaxe concise pour déclarer des classes qui sont de simples porteurs d'ensembles de données persistants et immuables.

Raisons et objectifs


Les plaintes selon lesquelles «Java est trop verbeux» et que vous devez «cérémonie» avec lui sont assez courantes. La raison en est que les classes sont conçues uniquement pour stocker un certain ensemble de données. Pour écrire correctement une telle classe, vous devez écrire beaucoup de code formel, répétitif et sujet aux erreurs: constructeurs, getters et setters, equals (), hashCode (), toString (), etc. Les développeurs trichent parfois et ne redéfinissent pas equals () et hashCode (), ce qui peut entraîner un comportement inhabituel ou des problèmes de débogage. Ou, lorsque les développeurs ne veulent pas déclarer une autre classe, ils prescrivent une alternative, mais pas tout à fait appropriée, simplement parce qu'elle a la «forme correcte».

Les environnements de développement aideront à enregistrer la plupart du code dans la classe, mais n'aideront pas le développeur à lire ce code pour naviguer rapidement parmi des dizaines de lignes de code standard et comprendre que cette classe est un support de données ordinaire. Les ensembles de données standard de modélisation de code Java doivent être simples à écrire, à comprendre et à valider.

À première vue, il peut sembler que les enregistrements visent à réduire le code du modèle. Nous y mettons l'objectif sémantique: «modéliser les données en tant que données» (modéliser les données en tant que données). Si la sémantique est correcte, le code du modèle fera tout par lui-même sans la participation du développeur. Après tout, déclarer des ensembles de données persistants devrait être facile, clair et concis.

Des objectifs qui n'étaient pas


Nous ne nous sommes pas fixé pour objectif de «déclarer la guerre» au code passe-partout. En particulier, nous n'avions pas l'intention de résoudre le problème des classes mutables en utilisant la convention de dénomination des composants JavaBean. Bien que les propriétés, la métaprogrammation et la génération de code basé sur des annotations soient souvent suggérées comme des «solutions» à ce problème, l'ajout de ces fonctionnalités n'était pas non plus notre objectif.

La description


Les entrées sont un nouveau type de déclaration de type en Java. Comme enum, l'écriture est une classe fonctionnellement limitée. Il annonce sa vue et fournit une API qui s'appuie sur cette vue. Les entrées ne séparent pas l'API de la présentation et, à leur tour, sont concises.

L'entrée contient un nom et une description de l'état. La description de l'état déclare les composants de cet enregistrement. Facultativement, l'enregistrement peut avoir un corps. Par exemple:

record Point(int x, int y) { } 

Les enregistrements sémantiques étant de simples supports de données, ils reçoivent automatiquement des éléments standard:

  • Champ final privé pour chaque composante d'état;
  • Une méthode de lecture publique pour chaque composant d'état avec le même nom et type que le composant;
  • Un constructeur public correspondant à la signature d'enregistrement; il initialise chaque champ à partir de l'argument correspondant;
  • Les implémentations de equals () et hashCode (), qui disent que deux enregistrements sont égaux s'ils sont du même type et contiennent le même état;
  • Une implémentation de toString (), qui comprend une représentation sous forme de chaîne de tous les composants d'enregistrement avec leurs noms.

En d'autres termes, la présentation du dossier est entièrement basée sur une description de l'état. En outre, en fonction de l'état de l'enregistrement, la formation de equals (), hashCode () et toString () se produit.

Limitations


Les enregistrements ne peuvent hériter d'aucune autre classe et ne peuvent pas déclarer de champs d'objet, à l'exception des champs finaux privés qui correspondent aux composants d'état. Tous les autres champs déclarés doivent être statiques. Ces limitations garantissent que la description de l'état définit en soi la vue.

Les inscriptions sont finales et ne peuvent être abstraites. Ces restrictions indiquent que l'API d'enregistrement est définie uniquement par une description d'état et ne peut pas être étendue ultérieurement avec une autre classe ou un autre enregistrement.

Les composants d'enregistrement sont finaux. Cette restriction met en œuvre le principe «inchangé par défaut», qui est largement utilisé pour les ensembles de données.

En plus des limitations mentionnées ci-dessus, les enregistrements se comportent comme des classes ordinaires: ils peuvent être déclarés comme de niveau supérieur ou imbriqués, ils peuvent être génériques, ils peuvent implémenter des interfaces. Les enregistrements sont créés en appelant le nouvel opérateur. Le corps d'écriture peut déclarer des méthodes statiques, des champs statiques, des blocs d'initialisation statiques, des constructeurs, des méthodes d'instance, des blocs d'initialisation d'instance et des types imbriqués. Un enregistrement et des composants d'état individuels peuvent être annotés. Si l'enregistrement est imbriqué, il est statique; cela élimine la situation avec des instances imbriquées qui pourraient automatiquement ajouter un état à l'enregistrement.

Entrées explicitement déclarées


Bien que l'implémentation standard des getters, ainsi que les méthodes equals (), hashCode () et toString (), soit acceptable dans la plupart des cas d'utilisation, le développeur a la possibilité de remplacer l'implémentation standard. Cependant, vous devez être particulièrement prudent lorsque vous remplacez les méthodes equals / hashCode.

Une attention particulière est portée à la déclaration explicite du constructeur canonique, dont la signature correspond à la description de l'état de l'enregistrement. Un constructeur peut être déclaré sans liste formelle de paramètres: dans ce cas, il est supposé qu'il coïncide avec la description de l'état, et tous les champs d'enregistrement sont implicitement initialisés par la fermeture standard du corps du constructeur des paramètres formels correspondants (this. X = x) à la sortie. Cela permet au constructeur canonique de vérifier et d'ajuster uniquement ses paramètres, ainsi que de sauter l'initialisation explicite du champ. Par exemple:

 record Range(int lo, int hi) { public Range { if (lo > hi) /* referring here to the implicit constructor parameters */ throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); } } 

La grammaire


 RecordDeclaration: {ClassModifier} record TypeIdentifier [TypeParameters] (RecordComponents) [SuperInterfaces] [RecordBody] RecordComponents: {RecordComponent {, RecordComponent}} RecordComponent: {Annotation} UnannType Identifier RecordBody: { {RecordBodyDeclaration} } RecordBodyDeclaration: ClassBodyDeclaration RecordConstructorDeclaration RecordConstructorDeclaration: {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName [Throws] ConstructorBody 

Annotations pour l'enregistrement des composants


Les annotations d'annotation peuvent être appliquées aux composants d'enregistrement si elles s'appliquent aux composants, paramètres, champs ou méthodes. Les annotations publicitaires qui s'appliquent à l'un de ces composants s'appliquent aux déclarations implicites de tous les éléments requis.

Les annotations de type qui modifient les types de composants d'enregistrement s'étendent aux types dans les déclarations implicites des éléments requis (par exemple, les paramètres du constructeur, les déclarations de champ et les méthodes). Les déclarations explicites des éléments requis doivent correspondre exactement au type du composant correspondant de l'enregistrement, sans inclure les annotations de type.

API de réflexion


Les méthodes publiques suivantes seront ajoutées à java.lang.Class :

  • RecordComponent [] getRecordComponents ()
  • booléen isRecord ()

La méthode getRecordComponents () renvoie un tableau java.lang.reflect.RecordComponent , où java.lang.reflect.RecordComponent est une nouvelle classe.

Les éléments de ce tableau correspondent aux composants de l'enregistrement et vont dans le même ordre dans lequel ils sont déclarés dans l'enregistrement. Des informations supplémentaires peuvent être extraites de chaque RecordComponent dans le tableau, y compris le nom, le type, le générique, ainsi que sa valeur.

La méthode isRecord () renvoie true si cette classe est déclarée en tant qu'enregistrement . (Similaire à la méthode isEnum () ).

Alternatives


Les enregistrements peuvent être définis comme la forme conditionnelle des tuples. Au lieu d'enregistrements, nous pouvons utiliser des tuples structurels. Bien que les tuples offrent des moyens plus légers d'exprimer certains ensembles de données, le résultat est souvent moins informatif:

  • Le principe principal de la philosophie Java est que les noms comptent . Les classes et leurs éléments portent des noms pertinents pour leur contenu, contrairement aux tuples et à leurs composants. Autrement dit, la classe Person avec les propriétés firstName et lastName est plus compréhensible et fiable que le tuple anonyme de String et String .
  • Les classes prennent en charge la validation d'état via leurs constructeurs, contrairement aux tuples. Certains ensembles de données, tels que les plages numériques, ont des invariants qui peuvent être référencés ultérieurement s'ils sont utilisés par le constructeur;
  • Les classes peuvent avoir un comportement basé sur leur état; la combinaison de l'état et du comportement rend le comportement lui-même plus explicite et accessible. Les tuples, n'étant qu'un ensemble de données, n'offrent pas une telle opportunité.

Dépendances


Les enregistrements vont bien avec les types isolés (JEP 360) ; avec les types isolés, les enregistrements forment une construction, souvent appelée types de données algébriques. De plus, les entrées elles-mêmes permettent la correspondance de modèles . Étant donné que les enregistrements associent leurs API à des descriptions d'état, nous pouvons finalement obtenir des modèles de déconstruction pour les enregistrements et utiliser les informations des classes isolées dans une instruction switch .

Source: https://habr.com/ru/post/fr484700/


All Articles