W3docs

Java Getters und Setters

Private Felder in Java sicher mit Getter- und Setter-Methoden freigeben und Validierung in Settern hinzufügen.

Ein Getter ist eine Methode, die den Wert eines Feldes zurückgibt; ein Setter ist eine Methode, die einen Wert in ein Feld schreibt. Zusammen bilden sie den Standardweg, um kontrollierten Zugriff auf ein privates Feld zu gewähren. Das vorherige Kapitel zur Kapselung hat das Warum erklärt — dieses Kapitel behandelt das Wie, einschließlich der Namenskonventionen, der Validierungsmuster und wann man sie ganz weglassen sollte.

Das grundlegende Muster

Ein Feld, ein Getter und ein Setter:

public class User {
  private String email;

  public String getEmail()              { return email; }
  public void   setEmail(String email)  { this.email = email; }
}

Per Konvention:

  • Der Getter heißt get<FieldName> für alles außer booleans.
  • Bei einem boolean-Feld heißt der Getter is<FieldName> (isActive, hasPermission).
  • Der Setter heißt set<FieldName> und nimmt einen Parameter vom Typ des Feldes entgegen.
  • Beide sind public, sofern kein Grund dagegen spricht.

Diese Regeln entsprechen auch der JavaBeans-Konvention — viele Werkzeuge (Serialisierungsbibliotheken, Template-Engines, IDE-Refactorings) verlassen sich darauf und sehen Felder, deren Accessoren anders benannt sind, schlicht nicht.

Validierung in Settern

Ein Setter ist die einzige Möglichkeit, fehlerhafte Eingaben abzulehnen, bevor das Feld beschrieben wird:

public class User {
  private String email;

  public void setEmail(String email) {
    if (email == null || !email.contains("@")) {
      throw new IllegalArgumentException("not a valid email: " + email);
    }
    this.email = email;
  }
}

Ein nackter Setter, der nur zuweist, ist kaum besser als ein öffentliches Feld. Der Sinn eines Setters zwischen Aufrufer und Feld besteht darin, dort eine Prüfung einbauen zu können. Wenn man sich dabei ertappt, Dutzende von Settern zu schreiben, die alle wie this.x = x; aussehen, sollte man überlegen, ob die Klasse überhaupt veränderbar sein muss — ein per Konstruktor gesetztes, nur-lesebares Objekt (eine unveränderliche Klasse oder ein Record) ist oft die bessere Wahl.

Berechnete und abgeleitete Getter

Ein Getter muss nicht eins zu eins einem Feld entsprechen. Er kann seinen Rückgabewert berechnen:

public class Rectangle {
  private final double width, height;
  public Rectangle(double w, double h) { this.width = w; this.height = h; }

  public double getWidth()  { return width; }
  public double getHeight() { return height; }
  public double getArea()   { return width * height; }   // derived
}

Für einen Aufrufer sieht getArea() genauso aus wie getWidth() — beides sind Methoden, die einen double zurückgeben. Die Tatsache, dass eine davon einen gespeicherten Wert liest und die andere ihn berechnet, ist ein Implementierungsdetail, das man ändern kann, ohne dass es jemand bemerkt.

Nur-lese-Getter

Ein Getter ohne Setter macht ein Feld lesbar, sperrt es aber für Schreibzugriffe:

public class Order {
  private final long id;
  private final long createdAt;

  public Order(long id, long createdAt) {
    this.id        = id;
    this.createdAt = createdAt;
  }

  public long getId()        { return id; }
  public long getCreatedAt() { return createdAt; }
}

Externer Code kann fragen „Was ist die ID?", kann sie aber nicht ändern. So funktionieren nach der Konstruktion unveränderliche Felder in der Praxis.

Boolean-Benennung: is, has, should

Boolean-Getter lesen sich mit dem Präfix is/has/can/should natürlicher:

public boolean isActive()        { return active; }
public boolean hasPermission()   { return permission != null; }
public boolean canRetry()        { return retries < maxRetries; }

In Kombination mit dem Feldnamen liest sich die Aufrufstelle wie ein englischer Satz: if (user.isActive()) { ... }. Der Setter dazu lautet einfach setActive(boolean), wobei das is wegfällt.

Ein Fallstrick: JavaBeans-Werkzeuge leiten den Property-Namen vom Accessor ab, daher bilden isActive() und getActive() beide auf die Property active ab — aber Werkzeuge suchen is-Accessor generell nur beim primitiven Typ boolean. Bei einem gebundenen Boolean-Feld sollte man bei getActive() bleiben, damit es auffindbar bleibt.

Defensive Kopien (nochmals)

Wenn ein Getter ein veränderbares internes Objekt zurückgeben würde, sollte eine Kopie oder eine nicht modifizierbare Ansicht zurückgegeben werden:

private final List<String> tags = new ArrayList<>();

public List<String> getTags() {
  return List.copyOf(tags);
}

Gleiches gilt beim Setter für den Eingang:

public void setTags(List<String> tags) {
  this.tags.clear();
  this.tags.addAll(tags);    // copy in
}

Ohne diese Kopien könnte der Aufrufer die interne tags-Liste mutieren und damit alles umgehen, was die Klasse sonst tut.

Modernes Java: kürzere Accessoren

Neuerer Java-Code (insbesondere in Bibliotheken, die nicht an JavaBeans-Werkzeuge gebunden sind) lässt das get-Präfix oft der Symmetrie halber weg — analog dazu, wie record-Accessoren benannt werden:

public class Point {
  private final int x, y;
  public Point(int x, int y) { this.x = x; this.y = y; }
  public int x() { return x; }
  public int y() { return y; }
}

Wenn man nicht an JavaBeans gebunden ist (kein Hibernate, keine Jackson-Standardeinstellungen, kein JSP), ist dieser Stil in Ordnung — und entspricht dem, was record Point(int x, int y) {} automatisch generieren würde. Man sollte sich für einen Stil pro Codebasis entscheiden und ihn konsequent anwenden. Einen vollständigen Überblick über den Record-Ansatz bietet moderne Records.

Nicht reflexartig generieren

Moderne IDEs generieren mit einem Tastendruck bereitwillig einen Getter und Setter für jedes Feld. Diesem Drang sollte man widerstehen. Jedes generierte Paar ist eine Designentscheidung:

  • Ein Getter macht ein Feld zugänglich — möchte man wirklich, dass Aufrufer es lesen?
  • Ein Setter eröffnet einen Schreibpfad — möchte man wirklich, dass Aufrufer es ändern?
  • Wenn beides bedingungslos ja ist, warum ist das Feld dann überhaupt privat?

Die richtige Antwort lautet oft „keines von beidem" — setBalance(int) durch deposit(int) und withdraw(int) ersetzen, die die tatsächlichen Operationen ausdrücken.

Ein vollständiges Beispiel

java— editable, runs on the server

Was kommt als Nächstes

Damit sind die Grundlagen einer einzelnen Klasse abgeschlossen — wie man sie deklariert, wie man ihre Member kontrolliert und wie man genau das Richtige nach außen freigibt. Das nächste Kapitel beginnt mit dem zweiten großen OOP-Konzept: der Vererbung, bei der eine Klasse auf einer anderen aufbaut, statt von vorne anzufangen. Weiter zu Java-Vererbung.

Übungen

Übung
Warum ist ein Setter in der Regel einem öffentlichen Feld vorzuziehen, selbst wenn der Setter nur zuweist?
Warum ist ein Setter in der Regel einem öffentlichen Feld vorzuziehen, selbst wenn der Setter nur zuweist?
Was this page helpful?