Vielleicht hast du bereits schon das ein oder andere Mal von Java Records gehört doch der richtige Anwendungsfalle erschließt sich dir nicht? Oder fehlt dir einfach noch ein bisschen Wissen um Records richtig einsetzen zu können?
Dann erkläre ich dir nun hier, schnell und einfach, wie du Java Records in deinen zukünftigen Projekten richtig einsetzen kannst.
Als Entwickler schreibst du oft eine Menge Boilerplate Code der dir hilft, die Sichtbarkeit deiner Variablen einzuschränken oder Zugriff von außen zu ermöglichen. Gerade für reine POJOs (Plain Old Java Objects) oder DTO (Data Transfer Objects). Solche Klassen dienen nur dazu, Daten zu von einer Stelle zur nächsten zu transportieren. Dazu besitzen sie in der Regel auch keine Logik.
Nehmen wir an, wir haben eine Klasse User die unsere gleichnamige Tabelle repräsentiert. Wir lesen User aus der Datenbank aus und benutzen das Objekt um die Userdaten zwischen unseren Klassen zu transferieren. Die Klasse User sieht dann im Normalfall in etwa so aus:
package de.magicmarcy.dto;
import java.util.Date;
import java.util.Objects;
public class User {
private int userId;
private String userName;
private String firstName;
private String lastName;
private String email;
private String password;
private Date birthDate;
public User(int userId, String userName, String firstName, String lastName, String email, String password, Date birthDate) {
this.userId = userId;
this.userName = userName;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
this.birthDate = birthDate;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User user)) return false;
return userId == userA.userId && Objects.equals(userName, user.userName) && Objects.equals(firstName, user.firstName)
&& Objects.equals(lastName, user.lastName) && Objects.equals(email, user.email)
&& Objects.equals(password, user.password) && Objects.equals(birthDate, user.birthDate);
}
@Override
public int hashCode() {
return Objects.hash(userId, userName, firstName, lastName, email, password, birthDate);
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", password='" + password + '\'' +
", birthDate=" + birthDate +
'}';
}
}
So sieht ein einfaches DTO sicher auch bei dir aus und wie du sehen kannst, ist das ziemlich viel Code, nur damit du 7 Felder durch die Gegend transportieren kannst.
Und genau an dieser Stelle kommen Java Records ins Spiel. Die exakt gleiche Klasse als Record:
package de.magicmarcy.dto;
import java.util.Date;
record User(int userId, String userName, String firstName, String lastName, String email, String password, Date birthDate) {}
Fertig! 3 Zeilen Code und du hast das gleiche Objekt mit all seinen Funktionalitäten* wie im oberen Beispiel. Das wirkt natürlich schon mal ganz anders und ohne zu suchen, erkennt man auf einen Blick was hier abgeht.
Du kannst das User-Objekt auf die gleiche Art und Weise erstellen wie vorher und auch auf die Variablen zugreifen. Hier gibt es aber eine kleine Besonderheit. Schauen wir uns das im Detail an:
User user = new User(1, "magicmarcy", "Marcy", "Magic", "user@magicmarcy.de", "123456", new Date(TimeUnit.SECONDS.toMillis(1220227200));
System.out.println(user.userName);
System.out.println(user.firstName);
System.out.println(user.lastName);
Wie du sehen kannst, greifst du hier nicht mittels "get" oder "set" auf die Variablen zu sondern einfach per Feld-Name.
* Die Besonderheit von Java Records ist aber hier, dass alle Variablen immer final sind und nur über den Konstruktor gesetzt werden können. Aus diesem Grund gibt es auch keinen Default-Konstruktor und du musst das Objekt immer mit allen Feldern erstellen. Das ist zwar ein kleiner aber entscheidender Unterschied, den du beim programmieren berücksichtigen musst. Du kannst also die einzelnen Felder deines User-Objekts nicht einzeln manipulieren sondern müsstest also ein neues Objekt mit der geänderten Variable erstellen.
Wie du siehst, sind Java Records ziemlich leicht zu benutzen und sparen dir jede Menge Code doch nicht in allen Situationen sind Records das, was du brauchst. Daher sind Records zwar ziemlich cool, man muss aber gut überlegen ob das Verhalten auch so ist, wie man es in seiner Logik verwenden kann.
Java Records sind ab Java 14 (JDK 14+) verfügbar.
