statements before super(...)statements before super(...)
HappyCoders Glasses

Flexible Constructor Bodies in Java 25: Code vor super()
ausführen

Sven Woltmann
Sven Woltmann
Aktualisiert: 8. Juni 2026

In diesem Artikel erfährst du:

  • wie du ab Java 25 (ab Java 22 als Preview-Feature) Code in Konstruktoren auch vor dem Aufruf von super(...) oder this(...) ausführen kannst,
  • welche Einschränkungen es dabei gibt,
  • was Prolog und Epilog eines Konstruktors sind,
  • und ob die Neuerungen auch für Records und Enums gelten.

Gehen wir einen Schritt zurück: Warum sollte man Code vor super(...) oder this(...) ausführen wollen?

Code in Konstruktoren – Status Quo vor Java 25

Die folgenden Beispiele zeigen zum einen Workarounds, die bisher erforderlich waren, um vor dem Aufruf von super() oder this() Parameter zu validieren oder zu berechnen – und zum anderen, was schief gehen konnte, wenn der Konstruktor der Elternklasse eine Methode aufruft, die in der Kindklasse überschrieben wird.

Use Case 1: Validierung von Parametern

Ein häufiger Use Case ist die Validierung von Parametern einer Kindklasse. Im folgenden Beispiel ruft der Konstruktor von Rectangle erst den Konstruktor der Elternklasse, Shape, auf und validiert und setzt danach die Breite und Höhe:

public class Shape {
  private final Color color;

  public Shape(Color color) {
    this.color = color;
  }
}

public class Rectangle extends Shape {
  private final double width;
  private final double height;

  public Rectangle(Color color, double width, double height) {
    super(color);
    if (width < 0 || height < 0) throw new IllegalArgumentException();
    this.width = width;
    this.height = height;
  }
}Code-Sprache: Java (java)

Effizienter wäre es allerdings, die Parameter zu validieren, bevor der Super-Konstruktor aufgerufen wird. Doch das ist bisher nur mit dem folgenden, extrem unschönen Workaround möglich:

public Rectangle(Color color, double width, double height) {
  super(validateParams(color, width, height));
  this.width = width;
  this.height = height;
}

private static Color validateParams(Color color, double width, double height) {
  if (width < 0 || height < 0) throw new IllegalArgumentException();
  return color;
}Code-Sprache: Java (java)

Use Case 2: Berechnung eines Arguments, das an mehrere Parameter übergeben wird

Ein weiterer Use Case ist die Berechnung von Werten, die an mehr als einen Superklassen-Konstruktorparameter weitergegeben werden sollen. Im folgenden Beispiel wollen wir ein Quadrat mit vorgegebener Fläche erzeugen (dass eine statische Factory-Methode mit aussagekräftigem Namen dafür geeigneter wäre als der Konstruktor wollen wir an dieser Stelle ignorieren):

public class Square extends Rectangle {
  public Square(Color color, int area) {
    super(color, Math.sqrt(area), Math.sqrt(area));
  }
}Code-Sprache: Java (java)

Um die Wurzel der Fläche nicht zweimal zu berechnen, müssten wir einen Hilfskonstruktor einführen:

public class Square extends Rectangle {
  public Square(Color color, int area) {
    this(color, Math.sqrt(area));
  }

  private Square(Color color, double sideLength) {
    super(color, sideLength, sideLength);
  }
}Code-Sprache: Java (java)

Das ist hier aber auch nur deshalb möglich, weil area vom Typ int ist. Wäre area wie sideLength vom Typ double, würde das nicht funktionieren, da wir dann zwei Konstruktoren mit identischer Signatur hätten.

Und wollten wir zuvor sichergehen, dass area nicht negativ ist, müssten wir eine dritte Methode einführen, da wir auch vor this(...) keinen anderen Code ausführen dürfen:

public class Square extends Rectangle {
  public Square(Color color, int area) {
    this(color, Math.sqrt(validateArea(area)));
  }

  private static double validateArea(int area) {
    if (area < 0) throw new IllegalArgumentException();
    return area;
  }

  private Square(Color color, double sideLength) {
    super(color, sideLength, sideLength);
  }
}
Code-Sprache: Java (java)

Es ist kaum noch ersichtlich, was dieser Code tut.

Use Case 3: Aufruf einer überschriebenen Methode im Super-Konstruktor

Wir bleiben beim Shape/Rectangle-Beispiel und fügen eine printMe()-Methode hinzu, die im Konstruktor von Shape aufgerufen und in Rectangle überschrieben wird:

public class Shape {
  private final Color color;

  public Shape(Color color) {
    this.color = color;
    printMe();
  }

  void printMe() {
    System.out.println("color = " + color);
  }
}

public class Rectangle extends Shape {
  private final double width;
  private final double height;

  public Rectangle(Color color, double width, double height) {
    super(color);
    if (width < 0 || height < 0) throw new IllegalArgumentException();
    this.width = width;
    this.height = height;
  }

  @Override
  void printMe() {
    super.printMe();
    System.out.println("width = " + width + ", height = " + height);
  }
}Code-Sprache: Java (java)

Wenn wir nun z. B. new Rectangle(Color.RED, 29.7, 21.0) aufrufen, dann wird nicht etwa color = RED und width = 29.7, height = 21.0 ausgegeben, sondern:

color = RED
width = 0.0, height = 0.0Code-Sprache: Klartext (plaintext)

Der Grund dafür ist, dass printMe() vom Shape-Konstruktor aufgerufen wird, bevor im Rectangle-Konstruktor width und height initialisiert werden. printMe() sieht also noch die Default-Werte von width und height, also jeweils 0,0.

Java-Code vor super(...) und this(...)

In Java 22 wurde – zunächst als Preview-Feature und unter dem Namen „Statements before super(…)“ – die Möglichkeit eingeführt, Code auch vor dem Aufruf von super(...) oder this(...) auszuführen.

Wir können damit zunächst die Validierung der Fläche vor den Aufruf von this(...) ziehen:

public class Square extends Rectangle {
  public Square(Color color, int area) {
    if (area < 0) throw new IllegalArgumentException();  // ⟵ Validation before `this`
    this(color, Math.sqrt(area));
  }

  private Square(Color color, double sideLength) {
    super(color, sideLength, sideLength);
  }
}Code-Sprache: Java (java)

Und auch den Hilfskonstruktor brauchen wir nicht mehr. Parametervalidierung und Berechnung der Seitenlänge können nun direkt im Konstruktor untergebracht werden:

public Square(Color color, int area) {
  if (area < 0) throw new IllegalArgumentException();  // ⟵ Validation before `super`
  double sideLength = Math.sqrt(area);                 // ⟵ Calculation before `super`
  super(color, sideLength, sideLength);
}Code-Sprache: Java (java)

Bei diesem Konstruktor ist auf einen Blick erkennbar, was der Code macht.

In einem überarbeiteten Preview in Java 23 wurde zudem die Möglichkeit geschaffen, vor dem Aufruf von super(...) Felder der Klasse zu initialisieren. Wir dürfen damit die Rectangle-Klasse so schreiben:

public class Rectangle extends Shape {
  private final double width;
  private final double height;

  public Rectangle(Color color, double width, double height) {
    this.width = width;    // ⟵ Field initialization before `super`
    this.height = height;  // ⟵ Field initialization before `super`
    super(color);
  }

  . . .
}Code-Sprache: Java (java)

Bei einem Aufruf von new Rectangle(Color.RED, 29.7, 21.0) liefert die vom Konstruktor aufgerufene printMe()-Methode nun die erwartete Ausgabe:

color = RED
width = 29.7, height = 21.0Code-Sprache: Klartext (plaintext)

Warum funktioniert das jetzt? Der Prolog – also this.width = width; und this.height = height; – wird vollständig ausgeführt, bevor super(color) und damit der Shape-Konstruktor läuft. Wenn dieser nun printMe() aufruft, sind width und height bereits gesetzt. Genau das war vorher unmöglich: Jeder Code stand zwangsläufig nach super(…) – und damit zu spät.

Allgemein gilt: Beim Erzeugen eines Objekts werden zuerst die Prologe von der Kindklasse aufwärts bis zur Elternklasse abgearbeitet, danach wird jeder Konstruktor ab dem Aufruf von super(…) ausgeführt, von der Elternklasse abwärts. Felder, die du im Prolog setzt, stehen also schon bereit, wenn der Super-Konstruktor zu arbeiten beginnt.

Flexible Constructor Bodies sind seit Java 25 als finalisiertes Feature verfügbar.

Konstruktor-Prolog und -Epilog

Der Block vor dem Aufruf von super(...) oder this(...) in einem Konstruktor wird Prolog genannt.

Code nach dem Aufruf von super(...) oder this(...) oder Code in einem Konstruktor ohne Aufruf von super(...) oder this(...) wird als Epilog bezeichnet.

Einschränkungen

Im Prolog darf der Code Felder initialisieren, aber nicht lesend auf Felder der Klasse zugreifen und keine nicht-statischen Methoden der Klasse aufrufen. Er darf außerdem keine Instanzen von nicht-statischen inneren Klassen erzeugen, da diese dann eine Referenz auf das potentiell uninitialisierte Elternobjekt haben würden.

Der Prolog des Konstruktors einer inneren Klasse darf hingegen uneingeschränkt auf Felder und Methoden der äußeren Klasse zugreifen.

Records und Enums

Records und Enums können zwar nicht von einer selbst gewählten Klasse erben, deren Konstruktoren können allerdings mit this(...) alternative Konstruktoren aufrufen.

Auch davor darf nun Code, der den oben genannten Einschränkungen standhält, ausgeführt werden.

Historie

Flexible Constructor Bodies wurden in folgenden JDK Enhancement Proposals definiert:

Fazit

Das Ausführen von Code vor super(...) oder this(...) erlaubt es, Felder zu initialisieren und Parameter zu validieren oder zu berechnen, bevor der Super-Konstruktor oder ein alternativer Konstruktor aufgerufen wird. Das macht den Code sicherer und ermöglicht deutlich ausdrucksstärkeren Code als die Workarounds, die wir bisher für solche Zwecke konstruieren mussten.

Flexible Constructor Bodies wurden in Java 25 finalisiert und stehen seitdem ohne Preview-Flag zur Verfügung. Falls du noch Java 22, 23 oder 24 einsetzt, kannst du das Feature dort als Preview mit --enable-preview --release <Version> aktivieren.

Musstest du auch schon komplizierte Workarounds implementieren, und wie findest du das neue Feature? Lass es mich über die Kommentarfunktion wissen!

Java-Schulungen
(online oder vor Ort)
»