Records in Java 14

Records in Java 14

Mit eingeschaltetem Preview-Flag ist in Java 14 nun die Verwendung von Records verfügbar. Records erlauben einfache Datenklassen, ohne großen Schreibaufwand zu implementieren - ähnlich der Data Classes in Kotlin.

Deklaration

Alle Eigenschaften werden im Record-Header deklariert.

public record Dog(String breed, String name) { }

Der Compiler erzeugt anschließend einen parametrisierten Konstruktor mit allen Eigenschaften, lesende Accessors und die Methoden toString(), hashCode() und, equals(Object o).

@Test
public void testBreed() {
    final Dog underTest = new Dog("Poodle", "Bernie");
   assertEquals("Poodle", underTest.breed());
}

@Test
public void testName() {
    final Dog underTest = new Dog("Poodle", "Bernie");
    assertEquals("Bernie", underTest.name());
}

@Test
public void testToString() {
    final Dog underTest = new Dog("Poodle", "Bernie");
    assertEquals("Dog[breed=Poodle, name=Bernie]", underTest.toString());
}

@Test
public void testIsEqual() {
    final Dog underTest = new Dog("Poodle", "Bernie");
    final Dog bernie = new Dog("Poodle", "Bernie");
    assertEquals(bernie, underTest);
}

@Test
public void testIsNotEqual() {
    final Dog underTest = new Dog("Poodle", "Bernie");
    final Dog mrPeanutbutter = new Dog("Labrador Retriever", "Peanutbutter");
    assertNotEquals(mrPeanutbutter, underTest);
}

@Test
public void testHashcode() {
    final Dog underTest = new Dog("Poodle", "Bernie");
    assertEquals(-1039379410, underTest.hashCode());
}

Das Record wie dessen Eigenschaften sind final. Ein Setzen von Eigenschaften oder Vererbung von Records ist also nicht möglich.

@Test
public void testIsFinal() throws NoSuchFieldException {
    assertTrue(Modifier.isFinal(Dog.class.getModifiers()));
    assertTrue(Modifier.isFinal(Dog.class.getDeclaredField("name").getModifiers()));
    assertTrue(Modifier.isFinal(Dog.class.getDeclaredField("breed").getModifiers()));
}

Zusätzliche Konstruktoren

Eigene Konstruktoren können ebenfalls erstellt werden, diese müssen allerdings sicherstellen, dass wirklich alle Objekteigenschaften initialisiert werden.

public Dog(String name) {
    this("Poodle", name);
}
@Test
public void testDefaultBreedConstructor() {
    final Dog underTest = new Dog("Bernie");
    assertEquals("Poodle", underTest.breed());
}

Kompakter Konstruktor

Ebenfalls neu sind die Compact Constructors. Ein solcher enthält keine Parameter und dient bspw. zur Validierung oder Normalisierung von Eigenschaften.

public Dog {
    if (breed == null) {
        throw new IllegalArgumentException();
    }
    if (name.equals("Peanutbutter")) {
        name = "Mr. Peanutbutter";
    }
}
@Test
public void testWithoutBreed() {
    assertThrows(IllegalArgumentException.class, () ->
        new Dog(null, "Bernie")
    );
}

@Test
public void testPeanutbutter() {
    final Dog underTest = new Dog("Labrador Retriever", "Peanutbutter");
    assertEquals("Mr. Peanutbutter", underTest.name());
}

Zusätzliche Methoden

Auch das Erstellen eigener Methoden ist erlaubt:

public boolean isPoodle() {
    return breed.equals("Poodle");
}
@Test
public void testIsPoodle() {
    final Dog underTest = new Dog("Poodle", "Bernie");
    assertTrue(underTest.isPoodle());
}

@Test
public void testIsNoPoodle() {
    final Dog underTest = new Dog("Labrador Retriever", "Peanutbutter");
    assertFalse(underTest.isPoodle());
}

Fazit

Records dürfte für viele Java-Entwickler*innen eine echte Erleichterung darstellen und erspart das händische Anlegen von den immer gleichen Gettern und Methoden - oder zumindest die Generierung durch die IDE.

Auch wenn Lombok viele weitere nützliche Features bietet, dürften die meisten Menschen sich weitgehend auf jene beschränkt haben, die nun durch Records geboten werden. So wird die Verwendung von Lombok nach der Standardisierung von Records sicher stark zurückgehen.

Ein kleines Sample-Projekt mit dem hier verwendeten Code ist auf GitHub verfügbar.

(Titelbild von Frédérique Voisin-Demery)