record

Podstawa JEP-359.
record (rekord) jest nowym słowem kluczowym. Podobnie jak enum (wyliczenie) oznacza klasę o ograniczonych możliwościach, podlegającą pewnym regułom.
Założeniem jest utworzenie prostej klasy, będącej agregatem danych.
record ma nazwę i opis:
record Point(double x, double y){};
Powyższy kod oznacza utworzenie klasy, która:

  • ma prywatne pola double x i double y oznaczone jako final
  • ma dwie metody odczytujące x() i y(). Te metody nie są getters’ami w rozumieniu JavaBean
  • ma publiczny konstruktor, który niejawnie ustawia wartość this.x i this.y odpowiednio na x i y
  • metodę equals oznaczoną jako final
  • metodę hashCode oznaczoną jako final
  • metodę toString
  • Ze względu na oznaczenia final klasa jest niezmienna (immutable) i żadna klasa nie może po niej dziedziczyć. Więcej o immutable.
  • Rekord rozszerza klasę java.lang.Record, a więc nie może rozszerzać żadnej innej klasy

Poza powyższymi ograniczeniami rekordy zachowują się jak normalne klasy:

  • Mogą być klasami najwyższego poziomu (top level) albo zagnieżdżonymi (nested)
  • Mogą być uogólnione (generic)
  • Mogą implementować interfejsy
  • Instancje są tworzone przy użyciu słowa kluczowego new
  • Mogą mieć dostęp publiczny (public) albo chroniony

W ciele rekordu można deklarować statyczne pola, statyczne metody, statyczne inicjalizatory, konstruktory, metody instancji, typy zagnieżdżone.
Rekordy i poszczególne komponenty rekordu mogą być oznaczane adnotacjami.
Zagnieżdżony rekord jest niejawnie static.
Komponenty klasy, które są niejawnie tworzone mogą też być tworzone jawnie.
Stosowne zmiany zostały dokonane w Reflection API.
Rekord implementujący Serializable jest serializowany i deserializowany nieco inaczej niż inne klasy. Szczegóły omówione są w opisie klasy java.lang.Record oraz w opisie klasy java.io.ObjectInputStream.
Na razie jest to cecha oznaczona jako preview.

Rekord Point
package java14changes;

record Point(double x, double y) {
}
Klasa MainPoint
package java14changes;

public class MainPoint {
    public static void main(String[] args){
        Point point = new Point(17.0, 18.0);        
        System.out.println(point);
    }
}

Na konsoli zobaczymy:

Point[x=17.0, y=18.0]
Klasa Geometry
package java14changes;

public class Geometry {
    record Point2D(double x, double y){}
}
Klasa MainGeometry
package java14changes;

public class MainGeometry {
    public static void main(String[] args){
        Geometry.Point2D point = new Geometry.Point2D(0.0,0.0);
        System.out.println(point);
    }
}

Na konsoli zobaczymy:

Point2D[x=0.0, y=0.0]
Struktura wewnętrzna rekordu Point
package java14changes;

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

    public Point(double x, double y) { /* compiled code */ }

    public java.lang.String toString() { /* compiled code */ }

    public final int hashCode() { /* compiled code */ }

    public final boolean equals(java.lang.Object o) { /* compiled code */ }

    public double x() { /* compiled code */ }

    public double y() { /* compiled code */ }
}
Struktura wewnętrzna klasy Geometry
package java14changes;

public class Geometry {
    public Geometry() { /* compiled code */ }

    static final class Point2D extends java.lang.Record {
        private final double x;
        private final double y;

        public Point2D(double x, double y) { /* compiled code */ }

        public java.lang.String toString() { /* compiled code */ }

        public final int hashCode() { /* compiled code */ }

        public final boolean equals(java.lang.Object o) { /* compiled code */ }

        public double x() { /* compiled code */ }

        public double y() { /* compiled code */ }
    }
}