CodeGym /Kurse /JAVA 25 SELF /Record: Syntax, Vorteile

Record: Syntax, Vorteile

JAVA 25 SELF
Level 22, Lektion 0
Verfügbar

1. Das Problem „DTO-Klassen“: Warum brauchen wir Record-Klassen?

Seien wir ehrlich: Wie oft haben Sie eine Klasse wie diese geschrieben?


public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "Point{" + "x=" + x + ", y=" + y + '}';
    }
}

Eigentlich nichts Kompliziertes, aber zählen Sie die Zeilen! Und nun stellen Sie sich vor, Sie haben 10 solche Klassen, und jede hat 5–6 Felder. Selbst die IDE wird müde, diesen Boilerplate-Code zu generieren. Und wenn Sie ein neues Feld hinzufügen möchten, müssen Sie Konstruktor, Getter, equals, hashCode, toString anpassen ... Langeweile, Routine und eine Fehlerquelle.

Solche Klassen nennt man DTO (Data Transfer Object) oder Value Object. Sie speichern einfach Daten — das war’s. Aber wegen des Boilerplate-Codes ist ihre Wartung mühsam.

Wenn Sie meinen, das sei kein Problem, warten Sie einfach, bis Sie 50 solcher Klassen auf einmal ändern müssen. Dann werden Sie sich an Record-Klassen mit besonderer Wärme erinnern!

2. Einführung in Records: Syntax und Magie von Java 16+

Seit Java 16 hat sich alles geändert. Es gibt einen neuen Klassentyp — record. Er ist speziell für Fälle gedacht, in denen man einfach einen Satz von Daten speichern möchte. Die Syntax ist fast wie bei Tupeln in anderen Sprachen.

Wie deklariert man einen Record?


public record Point(int x, int y) { }

Und ... das war’s! Sie haben soeben eine unveränderliche Klasse mit zwei Feldern, einem Konstruktor, Gettern, equals, hashCode und toString erstellt. Ohne unnötiges Geschreibsel.

Was macht Java „unter der Haube“?

  • Felder x und y werden private final.
  • Getter werden automatisch erzeugt: int x() und int y().
  • Konstruktor: public Point(int x, int y).
  • equals/hashCode: vergleichen alle Felder nach Wert.
  • toString: gibt eine Zeichenfolge der Form "Point[x=1, y=2]" zurück.

Man kann sagen: Ein Record ist ein „DTO auf Steroiden“: weniger Code, mehr Garantien, weniger Bugs.

Unveränderlichkeit (Immutability)

Alle Felder einer Record-Klasse sind automatisch final. Nach der Erstellung kann das Objekt nicht mehr geändert werden — das garantiert der Compiler.

Wenn Sie versuchen, einen Setter hinzuzufügen oder ein Feld nicht final zu machen — der Compiler wird Sie stoppen. Diese Fürsorge um Ihre Ruhe ist selten!

3. Beispiel für die Verwendung einer Record-Klasse

Normale Klasse (viel Code):


public class Client {
    private final String name;
    private final int id;

    public Client(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public String getName() { return name; }
    public int getId() { return id; }

    // equals, hashCode, toString ...
}

Record-Klasse (eine Zeile!):


public record Client(String name, int id) { }

Verwendung:


public class Main {
    public static void main(String[] args) {
        Client client = new Client("Ivan", 123);
        System.out.println(client.name()); // Ivan
        System.out.println(client.id());   // 123
        System.out.println(client);        // Client[name=Ivan, id=123]
    }
}

Beachten Sie:
- Die Zugriffsmethoden auf die Felder heißen genauso wie die Felder: name(), id().
- Es gibt kein setName() oder setId() — das Objekt kann nach der Erstellung nicht geändert werden.

4. Vorteile von Record-Klassen: weniger Code, weniger Fehler

Weniger Code — mehr Glück

Warum 40 Zeilen schreiben, wenn eine reicht? Record-Klassen sparen Zeit und Nerven, besonders in großen Projekten mit vielen DTOs und Value Objects.

Unveränderlichkeit „per Vertrag“

  • Record-Klassen sind immer final und unveränderlich.
  • Ein Objekt lässt sich nicht fälschen oder versehentlich ändern.
  • Keine „seltsamen“ Bugs, weil sich der Objektzustand an unerwarteter Stelle ändert.
  • Sicher in nebenläufigen Programmen einsetzbar (sofern alle Felder ebenfalls immutable sind).

Automatische Generierung von equals/hashCode/toString

Sie müssen Vergleich, Hash-Berechnung und schöne Ausgabe nicht von Hand schreiben. Alles wird automatisch und korrekt erledigt.


Client c1 = new Client("Anna", 42);
Client c2 = new Client("Anna", 42);

System.out.println(c1.equals(c2));                 // true
System.out.println(c1.hashCode() == c2.hashCode()); // true
System.out.println(c1);                             // Client[name=Anna, id=42]

Ideal für Collections und Schlüssel

Record-Objekte können als Schlüssel in HashMap, als Elemente in HashSet usw. verwendet werden — alles funktioniert korrekt, weil equals und hashCode alle Felder berücksichtigen.


import java.util.HashMap;
import java.util.Map;

Map<Client, String> clients = new HashMap<>();
clients.put(new Client("Anna", 42), "VIP");

System.out.println(clients.get(new Client("Anna", 42))); // VIP

Explizite Beschreibung der Daten

Die Syntax einer Record-Klasse zeigt sofort, welche Daten gespeichert werden und dass das Objekt unveränderlich ist. Das macht den Code für andere Entwickler verständlicher (und für Sie in sechs Monaten).

5. Tabelle: Vergleich einer normalen Klasse und einer Record-Klasse

Normale Klasse Record-Klasse
Syntax Viel Code Eine Zeile
Unveränderlichkeit Muss explizit umgesetzt werden Vom Compiler garantiert
Autogenerierte Methoden Nein Ja (equals, hashCode, toString)
Felder hinzufügbar Ja Nur Record-Komponenten
Vererbung Vererbung möglich Immer final, Vererbung nicht möglich
Einsatz in Collections Methoden korrekt implementieren Funktioniert „out of the box“

6. Wir entwickeln die Übungsanwendung weiter: Beispiel mit einer Record-Klasse

Angenommen, in Ihrer Lernbankanwendung müssen Buchungen gespeichert werden: Datum, Betrag, Art der Operation (z. B. „Einzahlung“ oder „Abhebung“).

Bis Java 16:


public class Transaction {
    private final LocalDate date;
    private final double amount;
    private final String type;

    public Transaction(LocalDate date, double amount, String type) {
        this.date = date;
        this.amount = amount;
        this.type = type;
    }

    public LocalDate getDate() { return date; }
    public double getAmount() { return amount; }
    public String getType() { return type; }

    // equals, hashCode, toString ...
}

Mit Java 16 und Record:


import java.time.LocalDate;

public record Transaction(LocalDate date, double amount, String type) { }

Verwendung:


Transaction t = new Transaction(LocalDate.now(), 100.0, "deposit");
System.out.println(t);         // Transaction[date=2024-06-01, amount=100.0, type=deposit]
System.out.println(t.amount()); // 100.0

7. Visualisierung: Was generiert ein Record

Schauen wir uns den „ausgeschriebenen“ Record an (in etwa das, was der Compiler generiert):


public final class Point extends java.lang.Record {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() { return x; }
    public int y() { return y; }

    @Override
    public boolean equals(Object o) { /* Vergleich anhand der Felder */ }

    @Override
    public int hashCode() { /* Berechnung anhand der Felder */ }

    @Override
    public String toString() { /* schöne Ausgabe */ }
}

Kurz: Wann sollte man Records verwenden

  • Wenn ein unveränderliches Objekt mit einem Satz von Daten benötigt wird.
  • Wenn Sie „ehrliche“ equals/hashCode/toString ohne Handarbeit brauchen.
  • Wenn Sie DTOs, Value Objects, Paare, Tripel, Farbe, Punkt, Bereich, Schlüssel für Collections usw. erstellen.

8. Typische Fehler bei der Arbeit mit Record-Klassen

Fehler Nr. 1: Versuch, einen Setter hinzuzufügen oder ein Feld nach der Erstellung zu ändern.
Eine Record-Klasse erlaubt es nicht, ihre Felder zu ändern. Wenn Sie versuchen, eine Methode wie setX(int x) hinzuzufügen, sagt der Compiler sofort „geht nicht“. Dasselbe gilt, wenn Sie versuchen, ein Feld direkt zu verändern.

Fehler Nr. 2: Versuch, ein nichtstatisches Feld hinzuzufügen.
In einer Record-Klasse dürfen nur Komponenten (Felder, die in den Klammern nach dem Record-Namen angegeben sind) und statische Felder deklariert werden. Gewöhnliche nichtstatische Felder sind nicht erlaubt — der Compiler lässt das nicht zu.

Fehler Nr. 3: Verwendung von Records für mutable Logik.
Record-Klassen sind nicht für Objekte mit veränderlichem Zustand gedacht. Wenn Sie nach der Erstellung etwas ändern müssen — verwenden Sie eine normale Klasse.

Fehler Nr. 4: Vergessen, dass ein Record immer final ist.
Eine Record-Klasse kann nicht abgeleitet werden und kann nicht als Superklasse dienen. Ein Verstoß gegen diese Einschränkung führt zu einem Compilerfehler. Merke: Versuchen wir nicht, einen Record zu „erweitern“ — er ist als abgeschlossener, unveränderlicher Typ konzipiert.

Fehler Nr. 5: Ignorieren der autogenerierten Methoden.
Wenn Sie equals, hashCode oder toString überschreiben, seien Sie vorsichtig — verletzen Sie deren Vertrag nicht, sonst funktionieren Collections und Vergleiche nicht korrekt.

1
Aufgabe
JAVA 25 SELF, Level 22, Lektion 0
Gesperrt
Erstellen einer digitalen Buchkarte 📚
Erstellen einer digitalen Buchkarte 📚
1
Aufgabe
JAVA 25 SELF, Level 22, Lektion 0
Gesperrt
Studentendaten anzeigen 🎓
Studentendaten anzeigen 🎓
1
Aufgabe
JAVA 25 SELF, Level 22, Lektion 0
Gesperrt
Überprüfung der Identität von Städten auf der Karte 🗺️
Überprüfung der Identität von Städten auf der Karte 🗺️
1
Aufgabe
JAVA 25 SELF, Level 22, Lektion 0
Gesperrt
Benutzerzugriffsverwaltung in der Anwendung 🔑
Benutzerzugriffsverwaltung in der Anwendung 🔑
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION