Java 14: Datensatzvorschau

In Kürze wird in Java 14 ein neues Syntaxfeature erscheinen - records. Nachdem ich die Vorschau studiert hatte , die kurz beschreibt, wie die Aufnahmen aussehen und was sie essen, wagte ich es, das Dokument für Habr auf Russisch anzupassen. Wen interessiert das - willkommen bei cat.


Zusammenfassung


Mit Einträgen können Sie die Funktionen von Java erweitern. Sie bieten eine kurze Syntax für die Deklaration von Klassen, die einfache Träger konstanter, unveränderlicher Datensätze sind.

Gründe und Ziele


Beschwerden, dass „Java zu ausführlich ist“ und dass Sie damit „zeremoniell“ umgehen müssen, sind weit verbreitet. Der Grund dafür sind Klassen, die nur zum Speichern eines bestimmten Datensatzes vorgesehen sind. Um eine solche Klasse korrekt zu schreiben, müssen Sie eine Menge formalen, sich wiederholenden und fehleranfälligen Code schreiben: Konstruktoren, Getter und Setter, equals (), hashCode (), toString () usw. Entwickler betrügen manchmal und definieren equals () und hashCode () nicht neu, was wiederum zu ungewöhnlichem Verhalten oder Problemen beim Debuggen führen kann. Oder wenn Entwickler keine weitere Klasse deklarieren möchten, schreiben sie eine Alternative vor, die jedoch nicht ganz geeignet ist, nur weil sie die „richtige Form“ hat.

Entwicklungsumgebungen tragen dazu bei, den größten Teil des Codes in der Klasse zu registrieren, jedoch nicht dazu, dass der Entwickler, der diesen Code liest, schnell zwischen Dutzenden von Codezeilen navigiert und versteht, dass diese Klasse ein gewöhnlicher Datenträger ist. Standarddatensätze für die Java-Codemodellierung sollten einfach zu schreiben, zu verstehen und zu validieren sein.

Auf den ersten Blick scheint es, dass die Datensätze dazu gedacht sind, den Vorlagencode zu reduzieren. Darin stecken wir das semantische Ziel: „Daten als Daten modellieren (Daten als Daten modellieren). Wenn die Semantik korrekt ist, erledigt der Vorlagencode alles von selbst ohne die Teilnahme des Entwicklers. Schließlich sollte die Deklaration persistenter Datensätze einfach, klar und präzise sein.

Ziele, die nicht waren


Wir haben uns nicht das Ziel gesetzt, dem Kesselschildcode „den Krieg zu erklären“. Insbesondere wollten wir das Problem der veränderlichen Klassen mit der Namenskonvention von JavaBean-Komponenten nicht lösen. Obwohl Eigenschaften, Metaprogrammierung und Codegenerierung auf der Grundlage von Anmerkungen häufig als „Lösungen“ für dieses Problem vorgeschlagen werden, war das Hinzufügen dieser Funktionen auch nicht unser Ziel.

Beschreibung


Einträge sind eine neue Art der Typdeklaration in Java. Schreiben ist wie Enum eine funktional begrenzte Klasse. Es kündigt seine Ansicht an und stellt eine API bereit, die auf dieser Ansicht aufbaut. Einträge trennen die API nicht von der Präsentation und sind wiederum präzise.

Der Eintrag enthält einen Namen und eine Beschreibung des Status. Die Zustandsbeschreibung deklariert die Komponenten dieses Datensatzes. Optional kann der Datensatz einen Body haben. Zum Beispiel:

record Point(int x, int y) { } 

Da semantische Datensätze einfache Datenträger sind, erhalten sie automatisch Standardelemente:

  • Privates Endfeld für jede Zustandskomponente;
  • Eine öffentliche Lesemethode für jede Statuskomponente mit demselben Namen und Typ wie die Komponente.
  • Ein öffentlicher Konstruktor, der mit der Datensatzsignatur übereinstimmt. es initialisiert jedes Feld anhand des entsprechenden Arguments;
  • Implementierungen von equals () und hashCode (), die besagen, dass zwei Datensätze gleich sind, wenn sie denselben Typ haben und denselben Status enthalten.
  • Eine Implementierung von toString (), die eine Zeichenfolgendarstellung aller Aufzeichnungskomponenten mit ihren Namen enthält.

Mit anderen Worten basiert die Darstellung der Aufzeichnung vollständig auf einer Beschreibung des Zustands. Basierend auf dem Status des Datensatzes werden außerdem equals (), hashCode () und toString () generiert.

Einschränkungen


Datensätze können keine andere Klasse erben und keine Objektfelder deklarieren, mit Ausnahme von privaten Endfeldern, die Statuskomponenten entsprechen. Alle anderen deklarierten Felder müssen statisch sein. Diese Einschränkungen stellen sicher, dass die Beschreibung des Zustands selbst die Ansicht definiert.

Beiträge sind endgültig und können nicht abstrakt sein. Diese Einschränkungen weisen darauf hin, dass die Datensatz-API nur durch eine Statusbeschreibung definiert wird und später nicht mit einer anderen Klasse oder einem anderen Datensatz erweitert werden kann.

Aufnahmekomponenten sind endgültig. Diese Einschränkung implementiert das Prinzip „Standardmäßig unverändert“, das häufig für Datensätze verwendet wird.

Zusätzlich zu den oben genannten Einschränkungen verhalten sich Datensätze wie normale Klassen: Sie können als oberste Ebene oder verschachtelt deklariert werden, sie können generisch sein, sie können Schnittstellen implementieren. Datensätze werden durch Aufrufen des neuen Operators erstellt. Der schreibende Body kann statische Methoden, statische Felder, statische Initialisierungsblöcke, Konstruktoren, Instanzmethoden, Instanzinitialisierungsblöcke und verschachtelte Typen deklarieren. Ein Datensatz und einzelne Statuskomponenten können mit Anmerkungen versehen werden. Wenn der Datensatz verschachtelt ist, ist er statisch. Dies beseitigt die Situation mit verschachtelten Instanzen, die dem Datensatz automatisch einen Status hinzufügen könnten.

Ausdrücklich deklarierte Einträge


Obwohl die Standardimplementierung von Getters sowie die Methoden equals (), hashCode () und toString () für die meisten Anwendungsfälle akzeptabel sind, kann der Entwickler die Standardimplementierung überschreiben. Sie sollten jedoch besonders vorsichtig sein, wenn Sie die equals / hashCode-Methoden überschreiben.

Besonderes Augenmerk wird auf die explizite Deklaration des kanonischen Konstruktors gelegt, dessen Signatur mit der Beschreibung des Status der Aufzeichnung übereinstimmt. Ein Konstruktor kann ohne formale Parameterliste deklariert werden. In diesem Fall wird davon ausgegangen, dass er mit der Statusbeschreibung übereinstimmt, und alle Datensatzfelder werden implizit initialisiert, indem der Konstruktorkörper standardmäßig aus den entsprechenden formalen Parametern (this. X = x) an der Ausgabe geschlossen wird. Auf diese Weise kann der kanonische Konstruktor nur seine Parameter überprüfen und anpassen sowie die explizite Feldinitialisierung überspringen. Zum Beispiel:

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

Grammatik


 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 

Anmerkungen für Aufnahmekomponenten


Anmerkungen Anmerkungen können auf Aufzeichnungskomponenten angewendet werden, wenn sie sich auf Komponenten, Parameter, Felder oder Methoden beziehen. Anzeigenanmerkungen, die für eine dieser Komponenten gelten, gelten für implizite Deklarationen aller erforderlichen Elemente.

Typanmerkungen, die die Typen von Datensatzkomponenten ändern, erstrecken sich auf Typen in impliziten Deklarationen erforderlicher Elemente (z. B. Konstruktorparameter, Felddeklarationen und Methoden). Explizite Deklarationen erforderlicher Elemente müssen genau mit dem Typ der entsprechenden Datensatzkomponente übereinstimmen, ohne Typanmerkungen.

Reflection API


Die folgenden öffentlichen Methoden werden zu java.lang.Class hinzugefügt:

  • RecordComponent [] getRecordComponents ()
  • boolean isRecord ()

Die Methode getRecordComponents () gibt ein Array java.lang.reflect.RecordComponent zurück , wobei java.lang.reflect.RecordComponent eine neue Klasse ist.

Die Elemente dieses Arrays entsprechen den Komponenten des Datensatzes und werden in derselben Reihenfolge deklariert, in der sie im Datensatz deklariert sind. Zusätzliche Informationen können aus jeder RecordComponent im Array extrahiert werden, einschließlich Name, Typ, generischer und Wert.

Die Methode isRecord () gibt true zurück , wenn diese Klasse als Datensatz deklariert ist. (Ähnlich der isEnum () Methode).

Alternativen


Datensätze können als bedingte Form von Tupeln definiert werden. Anstelle von Datensätzen können wir strukturelle Tupel verwenden. Obwohl Tupel leichtere Möglichkeiten zum Ausdrücken einiger Datensätze bieten, ist das Ergebnis oft weniger aussagekräftig:

  • Das Hauptprinzip der Java-Philosophie ist, dass Namen eine Rolle spielen . Klassen und ihre Elemente tragen Namen, die für ihren Inhalt relevant sind, während Tupel und ihre Komponenten dies nicht tun. Das heißt, die Person- Klasse mit den Eigenschaften firstName und lastName ist verständlicher und zuverlässiger als das anonyme Tupel aus String und String .
  • Klassen unterstützen die Statusvalidierung durch ihre Konstruktoren, Tupel nicht. Einige Datensätze, z. B. numerische Bereiche, haben Invarianten, auf die später verwiesen werden kann, wenn sie vom Konstruktor verwendet werden.
  • Klassen können sich basierend auf ihrem Status verhalten. Die Kombination von Zustand und Verhalten macht das Verhalten selbst expliziter und zugänglicher. Tupel, die nur ein Datensatz sind, bieten keine solche Möglichkeit.

Abhängigkeiten


Aufzeichnungen passen gut zu isolierten Typen (JEP 360) ; Datensätze bilden zusammen mit isolierten Typen ein Konstrukt, das häufig als algebraische Datentypen bezeichnet wird. Darüber hinaus ermöglichen die Einträge selbst den Mustervergleich . Da Datensätze ihre APIs mit Statusbeschreibungen verknüpfen, können wir letztendlich auch Dekonstruktionsmuster für Datensätze abrufen und die Informationen von isolierten Klassen in einer switch-Anweisung verwenden .

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


All Articles