JavaRush /Java-Blog /Random-DE /Kaffeepause Nr. 230. Was sind Datensätze in Java und wie ...

Kaffeepause Nr. 230. Was sind Datensätze in Java und wie funktionieren sie?

Veröffentlicht in der Gruppe Random-DE
Quelle: JavaTechOnline Dieser Artikel bietet einen detaillierten Einblick in das Konzept von Datensätzen in Java mit Beispielen, einschließlich ihrer Syntax, ihrer Erstellung und ihrer Verwendung. Kaffeepause Nr. 230.  Was sind Datensätze in Java und wie funktionieren sie - 1Records in Java ist eine der großartigen Funktionen, die erstmals in Java 14 als Vorschaufunktion eingeführt wurde und schließlich in der Version Java 17 veröffentlicht wurde. Viele Entwickler nutzen sie aktiv, was ihnen dabei hilft, eine große Menge an Boilerplate-Code erfolgreich zu reduzieren. Darüber hinaus müssen Sie dank Datensätzen keine einzige Codezeile schreiben, um eine Klasse unveränderlich zu machen.

Wann sollte Record in Java verwendet werden?

Wenn Sie unveränderliche Daten zwischen verschiedenen Ebenen Ihrer Anwendung übergeben möchten, kann die Verwendung von Record eine gute Wahl sein. Standardmäßig sind Datensätze in Java unveränderlich, was bedeutet, dass wir ihre Eigenschaften nach ihrer Erstellung nicht mehr ändern können. Dies hilft uns, Fehler zu vermeiden und verbessert die Zuverlässigkeit des Codes. Einfach ausgedrückt: Mit Record in Java können wir automatisch Klassen generieren.

Wo kann ich Record in Java verwenden?

Im Allgemeinen können wir Datensätze in jeder Situation verwenden, in der wir einfache Datencontainer mit unveränderlichen Eigenschaften und automatisch generierten Methoden deklarieren müssen. Im Folgenden sind beispielsweise einige Anwendungsfälle aufgeführt, in denen Datensätze nützlich sein können: Datenübertragungsobjekte (DTO): Wir können Record verwenden, um einfache Datenübertragungsobjekte zu deklarieren, die Daten enthalten. Dies ist nützlich, wenn Daten zwischen verschiedenen Anwendungsschichten übertragen werden, beispielsweise zwischen der Serviceschicht und der Datenbankschicht. Konfigurationsobjekte : Mit Record können Konfigurationsobjekte deklariert werden, die eine Reihe von Konfigurationseigenschaften für eine Anwendung oder ein Modul enthalten. Diese Objekte verfügen normalerweise über unveränderliche Eigenschaften, wodurch sie threadsicher und einfach zu verwenden sind. Wertobjekte. Mit Record können Wertobjekte deklariert werden, die eine Reihe von Werten enthalten, die ein bestimmtes Konzept oder Domänenmodell darstellen. API-Antworten : Beim Erstellen einer REST-API werden Daten normalerweise in Form von JSON oder XML zurückgegeben. In solchen Fällen müssen Sie möglicherweise eine einfache Datenstruktur definieren, die die API-Antwort darstellt. Datensätze sind hierfür ideal, da Sie damit eine leichte und unveränderliche Datenstruktur definieren können, die problemlos in JSON oder XML serialisiert werden kann. Testdaten. Beim Schreiben von Komponententests müssen Sie häufig Testdaten erstellen, die ein bestimmtes Szenario darstellen. In solchen Fällen müssen Sie möglicherweise eine einfache Datenstruktur definieren, die die Testdaten darstellt. Datensätze können hierfür ideal sein, da sie es uns ermöglichen, eine leichte und unveränderliche Datenstruktur mit minimalem Boilerplate-Code zu definieren. Tupelähnliche Objekte : Datensätze können verwendet werden, um Tupelähnliche Objekte zu deklarieren, die eine feste Anzahl zugeordneter Werte enthalten. Dies kann nützlich sein, wenn mehrere Werte von einer Methode zurückgegeben werden oder wenn mit Sammlungen verwandter Werte gearbeitet wird.

Was ist Record in Java?

Record ist in Java eine Klasse zum Speichern von Daten. Sie ähnelt einer traditionellen Java-Klasse, ist jedoch leichter und verfügt über einige einzigartige Funktionen. Datensätze sind standardmäßig unveränderlich, was bedeutet, dass ihr Status nach ihrer Erstellung nicht mehr geändert werden kann. Dadurch sind sie ideal für die Speicherung unveränderlicher Daten, wie z. B. Konfigurationsparameter oder Werte, die von einer Datenbankabfrage zurückgegeben werden. Es ist auch eine Möglichkeit, einen benutzerdefinierten Datentyp zu erstellen, der aus einer Reihe von Feldern oder Variablen sowie Methoden für den Zugriff auf und die Änderung dieser Felder besteht. Record in Java erleichtert die Arbeit mit Daten, indem es die Menge an Boilerplate-Code reduziert, die Entwickler immer wieder schreiben müssen. Die Syntax zum Erstellen eines Eintrags in Java lautet:
record Record_Name(Fields....)

Wie erstelle und verwende ich einen Datensatz in Java anhand von Beispielen?

Sehen wir uns an, wie man einen Datensatz in Java programmgesteuert erstellt und verwendet.

Einen Datensatz erstellen

Das programmgesteuerte Erstellen eines Datensatzes ähnelt nicht sehr dem Erstellen einer regulären Klasse in Java. Anstelle von class verwenden wir das Schlüsselwort „record“ . Sie müssen außerdem Felder mit Datentypen in Klammern des Datensatznamens deklarieren. Hier ist ein Codebeispiel, das zeigt, wie man einen Eintrag in Java erstellt:
public record Book(String name, double price) { }
In diesem Beispiel haben wir einen Datensatz namens „Book“ mit zwei Feldern erstellt: Name und Preis . Das Schlüsselwort public gibt an, dass auf diesen Eintrag außerhalb des Pakets zugegriffen werden kann, in dem er definiert ist.

Verwenden von Record

Um einen Datensatz in Java zu verwenden, können wir ihn mit dem Schlüsselwort new instanziieren, genau wie wir es mit einer regulären Klasse tun. Hier ist ein Beispiel:
Book book = new Book( "Core Java" , 324.25);
Hier wird eine neue Buchdatensatzinstanz erstellt, wobei das Feld „Name“ auf „Core Java“ und das Feld „Preis“ auf „ 324.25“ festgelegt ist . Sobald ein Datensatz erstellt wurde, kann auf seine Felder über den Namen des Felds selbst zugegriffen werden. Es gibt keine Get- oder Set-Methoden. Stattdessen wird der Feldname zum Methodennamen.
String name = book.name();

double price = book.price();
Hier sehen wir den Wert der Namens- und Preisfelder , die aus dem Buchdatensatz abgerufen werden . Zusätzlich zu den Feldern verfügen Datensätze auch über einige integrierte Methoden, mit denen Sie sie bearbeiten können. Zum Beispiel toString() , equal() und hashCode() . Denken Sie daran, dass Java-Datensätze standardmäßig unveränderlich sind, was bedeutet, dass ihr Status nach ihrer Erstellung nicht mehr geändert werden kann. Schauen wir uns ein weiteres Beispiel für die Erstellung und Verwendung von Datensätzen in Java an. Nehmen wir einen Fall, in dem wir einen Datensatz benötigen, um Informationen über einen Schüler zu speichern.
public record Student(String name, int age, String subject) {}
Dadurch wird ein Datensatz namens „ Student“ mit drei Feldern erstellt: Name , Alter und Fach . Wir können eine Instanz dieses Eintrags wie folgt erstellen:
Student student = new Student("John Smith", 20, "Computer Science");
Anschließend können wir die Feldwerte anhand des Feldnamens ermitteln:
String name = student.name();
int age = student.age();
String subject= student.subject();

Wie sieht der Datensatz nach der Kompilierung aus?

Da es sich bei Record nur um eine spezielle Klasse handelt, wird sie vom Compiler auch in eine reguläre Klasse umgewandelt, allerdings mit einigen Einschränkungen und Unterschieden. Wenn der Compiler nach dem Kompilierungsprozess einen Datensatz (Java-Datei) in Bytecode konvertiert, enthält die generierte .class- Datei einige zusätzliche Deklarationen der Record- Klasse . Unten sehen Sie beispielsweise den Bytecode, der vom Java-Compiler für den Student- Eintrag generiert wurde:
record Student(String name, int age) {   }

Schreiben in eine .class-Datei (nach der Kompilierung)

Wir können die unten konvertierte Klasse auch finden, wenn wir das Javap- Tool verwenden und den folgenden Befehl über die Befehlszeile anwenden. Es ist wichtig zu beachten, dass die Java-Befehlszeile das Javap- Tool enthält , mit dem Sie Informationen zu den Feldern, Konstruktoren und Methoden einer Klassendatei anzeigen können. >javap-Studentenfazit : Wenn wir den Bytecode sorgfältig prüfen, können wir einige Beobachtungen machen:
  • Der Compiler hat das Schlüsselwort Record durch class ersetzt .
  • Der Compiler hat die Klasse als final deklariert . Dies weist darauf hin, dass diese Klasse nicht erweitert werden kann. Dies bedeutet auch, dass es nicht vererbbar und unveränderlich ist.
  • Die konvertierte Klasse erweitert java.lang.Record . Dies zeigt an, dass alle Datensätze eine Unterklasse der im Paket java.lang definierten Record- Klasse sind .
  • Der Compiler fügt einen parametrisierten Konstruktor hinzu.
  • Der Compiler hat automatisch die Methoden toString() , hashCode() und equal() generiert .
  • Der Compiler hat Methoden für den Zugriff auf Felder hinzugefügt. Achten Sie auf die Namenskonventionen für Methoden – sie sind genau die gleichen wie die Feldnamen, es sollte kein get oder set vor den Feldnamen stehen .

Felder in Datensätzen

Datensätze in Java definieren ihren Status mithilfe einer Reihe von Feldern, die jeweils einen anderen Namen und Typ haben. Die Felder eines Datensatzes werden im Datensatzkopf deklariert. Zum Beispiel:
public record Person(String name, int age) {}
Dieser Eintrag besteht aus zwei Feldern: Name vom Typ String und Alter vom Typ int . Die Felder eines Datensatzes sind implizit endgültig und können nach der Erstellung des Datensatzes nicht neu zugewiesen werden. Wir können ein neues Feld hinzufügen, dies wird jedoch nicht empfohlen. Ein neues Feld, das einem Datensatz hinzugefügt wird, muss statisch sein. Zum Beispiel:
public record Person(String name, int age) {

   static String sex;
}

Konstrukteure in Aufzeichnungen

Wie herkömmliche Klassenkonstruktoren werden Datensatzkonstruktoren zum Erstellen von Datensatzinstanzen verwendet. Records verfügt über zwei Konstruktorkonzepte: den kanonischen Konstruktor und den kompakten Konstruktor . Der kompakte Konstruktor bietet eine präzisere Möglichkeit, Zustandsvariablen in einem Datensatz zu initialisieren, während der kanonische Konstruktor eine traditionellere Möglichkeit mit mehr Flexibilität bietet.

Kanonischer Konstruktor

Der Standard-Java-Compiler stellt uns einen All-Argument-Konstruktor (All-Field-Konstruktor) zur Verfügung, der seine Argumente den entsprechenden Feldern zuweist. Es ist als kanonischer Konstruktor bekannt. Wir können auch Geschäftslogik wie bedingte Anweisungen hinzufügen, um die Daten zu validieren. Nachfolgend finden Sie ein Beispiel:
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");
           }
      }
}

Kompakter Designer

Kompakte Konstruktoren ignorieren alle Argumente, einschließlich Klammern. Die entsprechenden Felder werden automatisch zugeordnet. Der folgende Code veranschaulicht beispielsweise das Konzept eines kompakten Konstruktors in 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");
          }
     }
}
Zusätzlich zum kompakten Konstruktor können Sie in Record reguläre Konstruktoren definieren , genau wie in einer regulären Klasse. Sie müssen jedoch sicherstellen, dass alle Konstruktoren alle Felder des Datensatzes initialisieren.

Methoden in Aufzeichnungen

Datensätze in Java generieren automatisch eine Reihe von Methoden für jedes Feld im Datensatz. Diese Methoden werden Zugriffsmethoden genannt und haben denselben Namen wie das Feld, dem sie zugeordnet sind. Wenn ein Datensatz beispielsweise über ein Feld namens „ price“ verfügt , verfügt er automatisch über eine Methode namens „ price()“ , die den Wert des Feldes „ price“ zurückgibt . Zusätzlich zu den automatisch generierten Zugriffsmethoden können wir in einem Eintrag auch eigene Methoden definieren, genau wie in einer regulären Klasse. Zum Beispiel:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
Dieser Eintrag verfügt über eine sayHello()- Methode, die eine Begrüßung unter Verwendung des Namensfelds ausgibt .

Wie Record dabei hilft, Boilerplate-Code zu reduzieren

Schauen wir uns ein einfaches Beispiel an, um zu sehen, wie Java-Notationen dabei helfen können, Boilerplate-Code zu eliminieren. Nehmen wir an, wir haben eine Klasse namens Person , die eine Person mit Namen, Alter und E-Mail-Adresse darstellt. So definieren wir eine Klasse auf traditionelle Weise. Code ohne Verwendung von 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 + '\'' +
      '}';
    }
}
Wie wir sehen können, erfordert diese Klasse eine Menge Boilerplate-Code, um die Felder, den Konstruktor, Getter, Setter und die toString()- Methode zu definieren . Nehmen wir außerdem an, wir möchten diese Klasse unveränderlich machen. Dazu müssen wir einige zusätzliche Schritte ausführen, wie zum Beispiel:
  • Deklarieren Sie die Klasse als final , damit sie nicht erweitert werden kann.
  • Deklarieren Sie alle Felder als privat und final , damit sie außerhalb des Konstruktors nicht geändert werden können.
  • Stellen Sie keine Setter- Methoden für Felder bereit.
  • Wenn eines der Felder veränderbar ist, sollten Sie eine Kopie davon zurückgeben, anstatt das ursprüngliche Objekt zurückzugeben.
Code mit Record :
public record Person(String name, int age, String email) {}
Das ist alles! Mit nur einer Codezeile haben wir eine Klasse definiert, die über dieselben Felder, Konstruktoren, Getter, Setter und die gleiche toString()- Methode wie eine herkömmliche Klasse verfügt. Die Record- Syntax übernimmt für uns den gesamten Boilerplate-Code. Aus dem obigen Beispiel wird deutlich, dass die Verwendung von Datensätzen in Java dazu beitragen kann, Boilerplate-Code zu eliminieren, indem eine präzisere Syntax für die Definition von Klassen mit einem festen Satz von Feldern bereitgestellt wird. Zum Definieren und Verwenden von Datensätzen ist weniger Code erforderlich als beim herkömmlichen Ansatz, und Datensätze bieten mithilfe von Methoden zum Aktualisieren der Felder direkten Zugriff auf Felder. Dadurch wird der Code besser lesbar, wartbar und weniger fehleranfällig. Darüber hinaus müssen wir mit der Record- Syntax nichts weiter tun, um die Klasse unveränderlich zu machen. Standardmäßig sind alle Felder in einem Datensatz endgültig und die Datensatzklasse selbst ist unveränderlich. Daher ist das Erstellen einer unveränderlichen Klasse mithilfe der Schreibsyntax viel einfacher und erfordert weniger Code als der herkömmliche Ansatz. Bei Datensätzen müssen wir Felder nicht als final deklarieren , keinen Konstruktor bereitstellen, der die Felder initialisiert, oder Getter für alle Felder bereitstellen.

Gemeinsame Datensatzklassen

Java kann auch generische Records- Klassen definieren . Eine generische Eintragsklasse ist eine Eintragsklasse, die über einen oder mehrere Typparameter verfügt. Hier ist ein Beispiel:
public record Pair<T, U>(T first, U second) {}
In diesem Beispiel ist Pair eine generische Datensatzklasse, die zwei Parameter vom Typ T und U akzeptiert . Wir können eine Instanz dieses Datensatzes wie folgt erstellen:
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Verschachtelte Klasse in Record

Sie können innerhalb eines Eintrags auch verschachtelte Klassen und Schnittstellen definieren. Dies ist nützlich für die Gruppierung verwandter Klassen und Schnittstellen und kann dazu beitragen, die Organisation und Wartbarkeit Ihrer Codebasis zu verbessern. Hier ist ein Beispiel für einen Eintrag, der eine verschachtelte Klasse enthält:
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;
       }
    }
}
In diesem Beispiel ist Person ein Eintrag, der eine verschachtelte Contact- Klasse enthält . Contact wiederum ist eine statische verschachtelte Klasse, die zwei private Endfelder enthält: E-Mail-Adresse und Telefonnummer. Es verfügt außerdem über einen Konstruktor, der eine E-Mail-Adresse und eine Telefonnummer akzeptiert, sowie über zwei Getter-Methoden: getEmail() und getPhone() . Wir können eine Personeninstanz wie folgt erstellen:
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
In diesem Beispiel haben wir ein neues Personenobjekt mit dem Namen John , Alter 30, und ein neues Kontaktobjekt mit der E-Mail-Adresse john@example.com und der Telefonnummer 123-456-7890 erstellt .

Verschachtelte Schnittstelle innerhalb von Record

Hier ist ein Beispieleintrag, der eine verschachtelte Schnittstelle enthält:
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
In diesem Beispiel ist Book der Eintrag, der die verschachtelte Edition- Schnittstelle enthält . Edition wiederum ist eine Schnittstelle, die eine einzelne getName()- Methode definiert . Wir können eine Book- Instanz wie folgt erstellen:
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
In diesem Beispiel erstellen wir ein neues Book- Objekt mit dem Titel „The Hitchhiker's Guide to the Galaxy“ von Douglas Adams und eine neue anonyme Implementierung der Edition- Schnittstelle , die den Namen „Science Fiction“ zurückgibt , wenn die Methode getName() aufgerufen wird .

Was kann Records sonst noch tun?

  • Einträge können benutzerdefinierte Konstruktoren definieren. Datensätze unterstützen parametrisierte Konstruktoren, die einen Standardkonstruktor mit bereitgestellten Parametern in ihren Körpern aufrufen können. Darüber hinaus unterstützen Datensätze auch kompakte Konstruktoren, die den Standardkonstruktoren ähneln, aber zusätzliche Funktionen wie Prüfungen im Hauptteil des Konstruktors enthalten können.
  • Wie jede andere Klasse in Java kann Record Instanzmethoden definieren und verwenden. Das bedeutet, dass wir für die Aufnahmeklasse spezifische Methoden erstellen und aufrufen können.
  • In Record ist das Definieren von Instanzvariablen als Klassenmitglieder nicht zulässig, da sie nur als Konstruktorparameter angegeben werden können. Datensätze unterstützen jedoch statische Felder und statische Methoden, die zum Speichern und Zugreifen auf Daten verwendet werden können, die allen Instanzen der Datensatzklasse gemeinsam sind.

Kann ein Datensatz Schnittstellen implementieren?

Ja, durch das Schreiben in Java können Schnittstellen implementiert werden. Der folgende Code veranschaulicht beispielsweise das Konzept:
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
Hier haben wir eine druckbare Schnittstelle mit einer einzigen print() -Methode definiert . Wir haben auch einen Person- Eintrag definiert, der die Printable- Schnittstelle implementiert . Der Personendatensatz verfügt über zwei Felder: Name und Alter und überschreibt die Druckmethode der druckbaren Schnittstelle , um die Werte dieser Felder zu drucken. Wir können einen Personeneintrag instanziieren und seine Druckmethode wie folgt aufrufen:
Person person = new Person("John", 30);
person.print();
Dadurch wird Name: John, Age: 30 an die Konsole ausgegeben . Wie im Beispiel gezeigt, können Java-Records Schnittstellen genauso implementieren wie reguläre Klassen. Dies kann nützlich sein, um einem Eintrag Verhalten hinzuzufügen oder um sicherzustellen, dass ein Eintrag einem durch eine Schnittstelle definierten Vertrag entspricht.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION