[JAVA] Die in JDK 14 veröffentlichte Vorschau der Datensätze ist nett, also versuchen wir es mit JShell

Einführung

Wenn Sie JDK 14 Rampdown: Build 27 in DZone lesen

One might say that JDK 14 Early Access Build 27 is the "records build."

Ich habe ** Records ** mit JShell ausprobiert, was ich endlich ausprobieren musste.

Bitte beachten Sie, dass sich der Inhalt möglicherweise geändert hat, als JDK14 veröffentlicht wurde und als Records danach offiziell veröffentlicht wurde.

JEP 359: Records

JEP 359: Records [^ 1] ist einer der Project Ambers [^ 2] zur Verbesserung der Java-Grammatik.

Java wird alle sechs Monate veröffentlicht, aber wie ursprünglich geplant werden Verbesserungen in Bezug auf verschiedene moderne Sprachen vorgenommen.

Unter diesen Umständen ist ** Records ** die [Constructor Shorthand] von TypeScript (https://maksimivanov.com/posts/typescript-constructor-shorthand/) und die [Primary Constructor] von Kotrin (https://kotlinlang.org). Wie in der einfachen Notation [^ 2], in der das Konstruktorargument von /docs/reference/classes.html#constructors auch als Definition der Feldvariablen, ** der Definition des Datensatznamens und des Arguments (Datensatzkomponente) sowie des Datencontainers dient Es ist ein Mechanismus zum Definieren der Datenklasse ** [^ 3].

Sobald dies verfügbar ist, können Klassen (und geeignete Feldmethoden) verwendet werden, um "nur einen" Datencontainer "mit Rückgabewerten, DTOs (Datenübertragungsobjekten), Stream-API-Operationen usw. zu erstellen. Zusätzlich zu der aktuellen Situation, dass "Sie eine Datenklasse erstellen müssen", wurde die Option "Sie können eine Datenklasse explizit als Container für Daten erstellen" hinzugefügt.

Verwendung von Datensätzen

Grundlegende Verwendung

Die Notation von Datensätzen wird auch in Qiita verwendet: "Änderungen in der Java-Syntax werden von Amber berücksichtigt" und "Was ist besser, Kotlin oder zukünftiges Java? ", aber

record Person(String name) {};

Wenn du schreibst

final class Person {

  //Feldvariablen
  public final String name;

  //Konstruktor mit Argumenten
  public Point(String name) {
    this.name = name;
  }

  //Getter nach der Feldvariablen, hashCode, equals,toString wird automatisch implementiert
  //getter ist ein fließender Methodenname (wie der Name der Feldvariablen).
  public String name() {
    return this.name;
  }
  public int hashCode() { /*Ausgelassen, implementiert, um die Äquivalenz aufrechtzuerhalten*/ }
  public boolean equals() { /*Ausgelassen, implementiert, um die Äquivalenz aufrechtzuerhalten*/ }
  public String toString() { /*Ausgelassen, gibt Klassennamen / Feldnamen / Wert zurück*/ }

  //das ist alles
}

Das heißt, eine Datenklasse, die der unveränderlichen Klasse entspricht, wird automatisch definiert. Praktisch.

Dies ist jedoch nicht nur eine Notation für die Klasse und eine Notation zum Lösen von Kesselplatten wie Lombok [^ 4], sondern ein neuer Mechanismus namens "Datencontainer" (ein Mechanismus für eingeschränkte Klassen wie Enum). ) Ist der Zweck [^ 5], und die Beseitigung der Redundanz ist nur das Ergebnis **, das auch in JEP geschrieben ist.

Wenn Sie es tatsächlich verwenden, scheint es besser zu sein, sich dieses Bereichs bewusst zu sein.

Schnittstellendeklaration

Verwenden Sie Geräte sowie Klasse.

Wenn Sie beispielsweise eine serialisierbare Schnittstelle definieren möchten,

record Person(String name) implements Serializable {};

Wenn du schreibst

final class Person implements Serializable {
 //Abkürzung
}

Dies bedeutet, dass eine serialisierbare Datenklasse definiert wurde.

Erbe

Es kann keine Oberklasse oder Unterklasse sein. Wie bereits erwähnt, handelt es sich um eine konventionelle Klassennotation.

Erhöhen Sie Ihre eigenen Konstruktoren und Methoden

Wenn Sie die Anzahl der Konstruktoren und Methoden erhöhen möchten, die nicht automatisch definiert werden, schreiben Sie sie in den Body (innerhalb von {}}).

record Person(String name){
  Person() {
    this("Jhon Doe");
  }
  public int getNameLength() {
    return name.length();
  }
} 

Im obigen Beispiel werden zusätzlich zu den automatisch vom Datensatz definierten Inhalten ein Konstruktor ohne Argumente und eine getNameLength-Methode vorbereitet.

Sie können die Anzahl der Konstruktoren, Klassenmethoden, Klassenfelder, Klasseninitialisierer, Instanzmethoden und Instanzinitialisierer erhöhen. Instanzfelder können nicht hinzugefügt werden, um die Form der Datenklasse nicht zu ändern [^ 6].

Versuchen Sie es mit JShell

Ich werde es mit JShell verwenden. Der PC, den ich ausprobiert habe, enthält JDK 14 Build 28 (14.ea.28-open), das mit SDKMAN installiert werden kann! Zum Zeitpunkt des Schreibens.

java -version
openjdk version "14-ea" 2020-03-17
OpenJDK Runtime Environment (build 14-ea+28-1366)
OpenJDK 64-Bit Server VM (build 14-ea+28-1366, mixed mode, sharing)

Wenn Sie es tatsächlich reproduzieren möchten, kopieren Sie es bitte und fügen Sie es ein, mit Ausnahme von jshell> und ...>.

1. Starten Sie jshell

Starten Sie JShell mit aktivierter Vorschau-Version.

jshell --enable-preview

2. Definieren und instanziieren Sie eine Datenklasse im Datensatz

Definieren wir eine Person-Datenklasse mit einem String-Namen in der Datensatzkomponente (dh dem Instanzfeld).

jshell> record Person(String name) {} ;

Die Datenklasse wird ohne Anzeige abgeschlossen. Lassen Sie uns sie also instanziieren.

jshell> var someone = new Person("Yamada");
someone ==> Person[name=Yamada]

Instanzen können jetzt mit der Variablen Someone referenziert werden.

Lassen Sie uns die Daten abrufen und die Methode verwenden.

jshell> System.out.println(someone.name());
Yamada
jshell> System.out.println(someone.toString());
Person[name=Yamada]

Lassen Sie uns auch die Äquivalenz überprüfen.

var other = new Person("Yamada");
   ...> someone.equals(other);
other ==> Person[name=Yamada]
$7 ==> true
jshell> other = new Person("Ichikawa");
   ...> someone.equals(other);
other ==> Person[name=Ichikawa]
$9 ==> false

Auch wenn die Instanzen unterschiedlich sind, können Sie die Äquivalenz der Feldwerte beurteilen.

3. Versuchen Sie, die Schnittstelle einzustellen

Erstellen wir eine Person und eine serialisierbare Person, um den Schnittstellentyp zu bestimmen.

someone instanceof Serializable;
|Error:
|Inkompatibler Typ:Person Java.io.Kann nicht in Serializable konvertiert werden:
|  someone instanceof Serializable;
|  ^-----^
jshell> record SerializablePerson(String name) implements Serializable {} ;
   ...> var nextOne = new SerializablePerson("Sato");
   ...> nextOne instanceof Serializable;
nextOne ==> SerializablePerson[name=Sato]
$16 ==> true

Eine Instanz von "SerializablePerson", dh "Implementierungen Serializable", hat ein "Instanz von" Ergebnis von "true", was darauf hinweist, dass die Schnittstelleneinstellungen wirksam sind.

4. Fügen Sie Ihre eigenen Konstruktoren und Methoden hinzu

Erhöhen wir den Standardkonstruktor im Personenkörper und fügen eine Methode hinzu, die die Datensatzkomponente verwendet.

jshell> record Person(String name) {
   ...>         Person() {
   ...>             this("Jhon Doe");
   ...>         }
   ...>         public int getNameLength() {
   ...>             return name.length();
   ...>         }
   ...>     }

Wenn Person mit dem Standardkonstruktor instanziiert wird, wird sie mit Jhon Doe initialisiert. Wir haben auch eine Instanzmethode hinzugefügt, die die Länge zurückgibt.

Lassen Sie uns es instanziieren und verwenden.

jshell> var someone = new Person();
   ...> someone.name();
   ...> someone.getNameLength();
someone ==> Person[name=Jhon Doe]
$3 ==> "Jhon Doe"
$4 ==> 8

Die Standardkonstruktoreinstellungen und die erweiterten Methoden funktionieren.

5. Andere

Class.isRecord()

Sie können isRecord überprüfen, um festzustellen, ob es sich um eine aus einem Datensatz erstellte Datenklasse handelt.

jshell> Person.class.isRecord();
$8 ==> true
jshell> String.class.isRecord();
$9 ==> false

Class.getRecordComponents()

Gibt die Datensatzkomponente der Datenklasse zurück, die aus dem Datensatz als Array von RecordComponent erstellt wurde.

jshell> record Range(int lo, int hi) {};

jshell> Range.class.getRecordComponents();
$12 ==> RecordComponent[2] { int lo, int hi }

6. Anwendungsbeispiel

Als Verwendungsbeispiel habe ich ein Beispiel für die Zwischenoperation von Stream in Hintergrundtext von JEP ausprobiert.

record Person(String name) {} ;

record PersonX(Person p, int hash) {
  PersonX(Person p) {
    this(p, p.name().toUpperCase().hashCode());
  }
}

//Ich denke, es sollte aus einer Datenquelle stammen
var list = List.of(new Person("Yamada"), 
  new Person("Ichikawa"), 
  new Person("Sato"), 
  new Person("Tanaka"));
       
list.stream()
  .map(PersonX::new)
  .sorted(Comparator.comparingInt(PersonX::hash))
  .peek(System.out::println)
  .map(PersonX::p)
  .collect(Collectors.toList());

Klicken Sie hier, um die Ergebnisse von J Shell anzuzeigen.

list ==> [Person[name=Yamada], Person[name=Ichikawa], Pers ... ato], Person[name=Tanaka]]
PersonX[person=Person[name=Tanaka], hash=-1827701194]
PersonX[person=Person[name=Yamada], hash=-1684585447]
PersonX[person=Person[name=Ichikawa], hash=-159644485]
PersonX[person=Person[name=Sato], hash=2537801]
$16 ==> [Person[name=Tanaka], Person[name=Yamada], Person[name=Ichikawa], Person[name=Sato]]

Obwohl das Ausführungsergebnis in JShell leicht weggelassen wird, ist es ein Mechanismus, um die Liste der (Daten-) Klassen mit Yamada, Ichikawa, Sato, Tanaka in PersonX zu konvertieren und sie in Hash-Reihenfolge zu sortieren [^ 7].

Person und PersonX sind Datenklassen, die mit ** record erstellt wurden, unterscheiden sich jedoch von normalen Klassen, aber das Framework der Klasse liegt nicht außerhalb des Bereichs. Selbst wenn Sie es verwenden, können Sie es mit normalen Klassen mischen und die Grammatik bis jetzt erweitern. Sie können es verwenden, ohne es zu ändern **.

In der Vergangenheit mussten Person und PersonX Klassen definieren und Methoden entsprechend veränderlich / unveränderlich vorbereiten ... aber wenn es als "Datencontainer" behandelt werden kann,

record Person(String name) {} ;
record PersonX(Person p, int hash) {
  PersonX(Person p) {
    this(p, p.name().toUpperCase().hashCode());
  }
}

Schreib es einfach.

Sie können auch Class # isRecord verwenden, um festzustellen, ob Sie zwischen einer Klasse und einem "Datencontainer" unterscheiden müssen.

Durch die Verwendung von ** Datensätzen auf diese Weise scheint es möglich zu sein, die Unterscheidung zwischen "(bloßem) Datencontainer" und herkömmlichen Klassen in Java zu integrieren und die Beschreibung einfach zu halten **.

abschließend

Mit JShell habe ich versucht, Datensätze zu erleben, die von JDK14 mit JShell in der Vorschau angezeigt wurden.

Um ehrlich zu sein, dachte ich zuerst "Kesselplattenmaße! Schön!", Aber als ich den JEP-Text las und ihn tatsächlich verwendete, erschien er genauso wie "Aufzählung" im Text. Der Aspekt des neuen "Datencontainer" -Mechanismus passt besser.

Natürlich gibt es eine Möglichkeit, die Datenklasse von Kotlin schnell zu mischen und zu verwenden, aber JEP 359 scheint ein Symbol dafür zu sein, dass Java immer einfacher zu verwenden ist, selbst wenn es Standard ist.

Wir freuen uns auf JDK14 und die offizielle Veröffentlichung danach.

[^ 1]: Für diejenigen, die nicht gut Englisch können kagamihoges Tagebuch --JEP 359: Übersetzte Aufzeichnungen (Vorschau) in Text Lassen Sie uns einen Überblick mit bekommen. [^ 2]: Der spezifische Inhalt von Amber ist leicht zu verstehen, wenn Sie Änderungen in der Java-Syntax, die von Amber berücksichtigt werden von Naoki Kishida lesen. .. [^ 3]: Hintergrundtext von JEP enthält Kotlins Datenklasse, Scalas Fallklasse und C # -Datensatzklasse. Es wurde als Beispiel für eine Datenklasse angegeben. [^ 4]: Aus JEP 359: Datensätze,
* "Während es oberflächlich verlockend ist, Datensätze so zu behandeln, dass es in erster Linie um die Reduzierung von Boilerplates geht, haben wir stattdessen Wählen Sie ein semantischeres Ziel: Modellieren von Daten als Daten. (Wenn die Semantik stimmt, kümmert sich das Boilerplate um sich selbst.) "* [^ 5]: Aus JEP 359: Records,
* Records sind eine neue Art der Typdeklaration in der Java-Sprache. Wie eine Aufzählung, a Datensatz ist eine eingeschränkte Form der Klasse. * [^ 6]: Aus JEP 359: Datensätze
* "Der Datensatzkörper kann statische Methoden, statische Felder, statische Initialisierer, Konstruktoren und Instanzen deklarieren Methoden, Instanzinitialisierer und verschachtelte Typen. "* [^ 7]: Ich habe Tippfehler im Originalcode geändert, das Limit entfernt und der Übersichtlichkeit halber Peek hinzugefügt.

Recommended Posts

Die in JDK 14 veröffentlichte Vorschau der Datensätze ist nett, also versuchen wir es mit JShell
Java 10 (JDK 10) wurde am 20. März 2018 veröffentlicht. Probieren wir also Hello World aus
Informationen zu Datensätzen, die zur Vorschau in Java JDK 14 hinzugefügt wurden
Selbst wenn Sie in Java Try-with-Resources in der try-Klausel zurückkehren, wird diese ordnungsgemäß geschlossen. Kehren wir also ohne Bedenken zurück