Records

Motivation

Menschen, die andere Programmiersprachen kennen sind häufig davon erstaunt, wie viel Aufwand man in Java betreiben muss, um in einer Klasse ein paar Daten zu speichern. Will man beispielsweise ein Klasse für Rechtecke schreiben, so kommt der folgende Code dabei heraus:

public final class Rectangle {

    private final double length;
    private final double width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    public double length() { return this.length; }
    public double width()  { return this.width; }

    @Override
    public boolean equals(Object o) {
        // ...
    }
    @Override
    public int hashCode() {
        // ...
    }

    @Override
    public String toString() {
        return "Rechteck{" + "length=" + length + ", width=" + width + '}';
    }
}
Nutzung
Rectangle r = new Rectangle(4, 3);
System.out.println("Fläche: " + r.length() * r.width());

Man sieht sofort, dass der Schreibaufwand erheblich ist, obwohl es sich hier um einen absolut primitiven Datencontainer handelt.

Um das Schreiben von einfachen Datenklassen zu vereinfachen, wurde mit Java 14 die Records eingeführt.

Records

Records

  • wurden mit Java 14 eingeführt
  • bieten eine einfache Möglichkeit, Datenklassen zu definieren
  • Syntax: record <NAME> (<ATTRIBUTE>) { <RUMPF> }
record Rectangle(double length, double width) { }
Nutzung
Rectangle r = new Rectangle(4, 3);
System.out.println("Fläche: " + r.length() * r.width());

Das Ergebnis dieser Deklaration ist identisch mit dem umfangreichen Java-Code, der im Beispiel davor gegeben wurde. Im Hintergrund werden vom Java-Compiler die notwendigen Methoden ergänzt.

Generiert werden

  • Klasse (final), abgeleitet von java.lang.Record
  • Attribute (final und private)
  • Konstruktor mit passender Signatur
  • Zugriffsmethoden, die ebenso heißen, wie die Attribute
  • equals- und hashCode-Methode
  • toString-Methode
Records sind final, man kann also nicht von ihnen erben

Konstruktor

Der Konstruktor von Records wird automatisch erstellt und als canonical constructor bezeichnet. Er nimmt Parameter, die den Attributen bei der Definition des Records entsprechen und weist dann die Werte zu.

Wenn man die Attribute prüfen möchte, bevor man sie zuweist, kann man einen eigenen Konstruktor schreiben, der eine entsprechende Prüfung durchführt.

  • Eigener canonical constructor mit Prüfung der Parameter
record Rectangle(double length, double width) {
    public Rectangle(double length, double width) {
        if (length <= 0 || width <= 0) {
            throw new java.lang.IllegalArgumentException(
                    String.format("Invalid dimensions: %f, %f", length, width));
        }
        this.length = length;
        this.width = width;
    }
}

Wenn man sich das Beispiel ansieht, fällt auf, dass die Attribute in der Record-Deklaration und im Konstruktor zwei mal angegeben werden und damit wieder unnötige Schreibarbeit entsteht. Aus diesem Grund, erlauben es Records, diese Dopplung einfach wegzulassen.

compact constructor

  • Wiederholung der Parameterliste entfällt
  • Zuweisung der Attribute wird weggelassen
record Rectangle(double length, double width) {
    public Rectangle {
        if (length <= 0 || width <= 0) {
            throw new java.lang.IllegalArgumentException(
                    String.format("Invalid dimensions: %f, %f", length, width));
        }
    }
}

Bei dieser Form entfällt die Zuweisung der Konstruktor-Parameter an die Attribute und wird vom Compiler automatisch ergänzt.

Methoden

Da Records im Endeffekt normale Java-Klassen sind, bei denen der Compiler uns einen großen Teil der Arbeit abnimmt, kann man sie auch mit eigenen Methoden versehen.

Man kann eigene Methoden schreiben

  • um die automatisch generierten zu ersetzen
  • um neue Methoden hinzuzufügen (auch static)
record Rectangle(double length, double width) {
    public double length() {
        return length;
    }
}

Das obige Beispiel ist zwar nicht besonders sinnvoll, zeigt aber die Möglichkeit, die bereits existierenden Zugriffsmethode length() durch eine eigene Implementierung zu ersetzen.

record Rectangle(double length, double width) {
    public double area() {
        return length * width;
    }
}

Copyright © 2025 Thomas Smits