
In this article, you will learn:
- how to execute code in constructors before calling
super(...)orthis(...)as of Java 25 (available as a preview feature since Java 22), - what restrictions exist,
- what the prologue and epilogue of a constructor are,
- whether the new features also apply to records and enums,
- and what to do if the compiler rejects the feature with an error.
Let’s take a step back: Why would you want to execute code before super(...) or this(...)?
Code in Constructors – Status Quo Before Java 25
The following examples show workarounds that were required up to Java 24 to validate or compute parameters before calling super(...) or this(...) – and also what could go wrong if the constructor of the parent class calls a method that is overridden in the child class.
Use Case 1: Parameter Validation
An everyday use case is the validation of parameters of a child class. In the following example, the constructor of Rectangle first calls the constructor of the parent class, Shape, and then validates and sets the width and height:
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;
}
}
However, validating the parameters before the super constructor is called would be much more efficient. Yet, up to Java 24, this was only possible with the following extremely unappealing workaround:
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;
}
Use Case 2: Calculation of an Argument That Is Passed to Several Parameters
Another use case is the calculation of values that are to be passed on to more than one superclass constructor parameter. In the following example, we want to create a square with a given area (we will ignore the fact that a static factory method with a meaningful name would be more suitable than a constructor):
public class Square extends Rectangle {
public Square(Color color, int area) {
super(color, Math.sqrt(area), Math.sqrt(area));
}
}
To avoid calculating the square root of the area twice, we would have to introduce an auxiliary constructor:
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);
}
}
However, this is only possible here because area is of the type int. If area were like sideLength of type double, this would not work, as we would then have two constructors with identical signatures.
And if we wanted to make sure that area is not negative beforehand, we would have to introduce a third method, as we are not allowed to execute any other code before this(...):
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);
}
}
It is hard to see what this code does.
Use Case 3: Calling an Overridden Method in the Super Constructor
We stay with the Shape/Rectangle example and add a printMe() method, which is called in the constructor of Shape and overridden in Rectangle:
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);
}
}
If we now call new Rectangle(Color.RED, 29.7, 21.0), the output is not color = RED and width = 29.7, height = 21.0, but:
color = RED
width = 0.0, height = 0.0
The reason for this is that printMe() is called by the Shape constructor before width and height have been initialized in the Rectangle constructor. printMe() therefore still sees the default values of width and height, i.e., 0.0.
Java Code Before super(...) and this(...)
Java 22 introduced – initially as a preview feature and under the name “Statements before super(...)” – the possibility of executing code before calling super(...) or this(...).
That means that we can now validate the area before calling this(...):
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);
}
}
And we no longer need the auxiliary constructor either. We can now accommodate the parameter validation and the calculation of the side length directly in the constructor:
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);
}
With this constructor, you can see at a glance what the code does.
Java 23 introduced the possibility of initializing fields before calling super(...). This allows us to write the Rectangle class in the following way:
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);
}
. . .
}
When calling new Rectangle(Color.RED, 29.7, 21.0), the printMe() method invoked by the constructor now prints the expected result:
color = RED
width = 29.7, height = 21.0
Why does this work now? The prologue – this.width = width; and this.height = height; – runs in full before super(color), and therefore before the Shape constructor. So when that constructor calls printMe(), width and height are already set. Up to and including Java 22, that was impossible: assignments to fields inevitably came after super(...) – and therefore too late.
In general: when an object is created, the prologues are processed first, from the subclass all the way up to Object; after that, each constructor runs from its super(...) call onward, from Object down. So the fields you set in the prologue are already in place by the time the superclass constructor begins its work.
Flexible Constructor Bodies have been available as a finalized feature since Java 25.
Constructor Prologue and Epilogue
The block before super(...) or this(...) is called the “prologue”.
Code after calling super(...) or this(...) or code in a constructor without calling super(...) or this(...) is called the “epilogue”.
The following diagram shows the order in which prologues and epilogues run for new Square(color, area). Each row is a class, from Square at the bottom to Object at the top. The prologue is on the left, the epilogue on the right, and in the middle is the call with which the constructor starts the constructor of the superclass. The constructor of Shape has no explicit call – an implicit super() precedes its epilogue, and its prologue is empty.
Restrictions
In the prologue, the code may initialize fields but must not read any fields of the class and must not call any non-static methods of the class. It must also not create instances of non-static inner classes, because they would hold a reference to the enclosing object – and that is the very instance that is still under construction.
The prologue of the constructor of an inner class, on the other hand, can access fields and methods of the outer class without restriction.
The prologue may only assign fields that have no initializer in their declaration. A field initializer such as = 0 runs – so as not to change the existing behavior – only after the superclass constructor has returned and would overwrite the value from the prologue. The width and height fields of Rectangle have no initializer – that is why the prologue may set them.
The following class tries to set a field with an initializer in the prologue:
public class Counter {
private int count = 0;
public Counter(int count) {
this.count = count;
super();
}
}
The compiler rejects the assignment:
Counter.java:5: error: assignment to initialized field 'count' may only appear after an explicit constructor invocation
this.count = count;
^
That is the message since Java 26. Java 25 phrases it as cannot assign initialized field 'count' before supertype constructor has been called.
In addition, the prologue must not contain a return statement and must not access fields or methods of the superclass via super.. Throwing an exception, on the other hand, is allowed – that is the fail-fast case from the Square example.
Records and Enums
While records and enums can’t extend a class of your choosing, their constructors can still invoke alternative constructors via this(...).
Code that complies with the above-mentioned restrictions may now also be executed before that.
For records, this only applies to the non-canonical constructors: the canonical constructor must not contain a this(...) call.
Compiler Error: “Flexible Constructor Bodies is only available with source level 25 and above”
Eclipse and the Java extension for Visual Studio Code report this error for the code above when the project’s source level is below 25:
The Java feature 'Flexible Constructor Bodies' is only available with source level 25 and above
The cause is the source level of the project, not the installed JDK – both IDEs use the Eclipse compiler, which phrases the message this way.
javac phrases it differently. With a source level below 25 – because the build tool still sets --release 21, for example – it reports:
Rectangle.java:8: error: flexible constructors is not supported in -source 21
this.width = width;
^
(use -source 25 or higher to enable flexible constructors)
On Java 23 and 24, where the feature was still a preview, the message reads:
Rectangle.java:8: error: flexible constructors is a preview feature and is disabled by default.
this.width = width;
^
(use --enable-preview to enable flexible constructors)
Java 22 reports something different for the Rectangle example – even with --enable-preview:
Rectangle.java:8: error: cannot reference this before supertype constructor has been called
this.width = width;
^
The preview in Java 22 only allowed statements before super(...) that do not set fields – assignments to fields came with Java 23. For the Square example, which only checks and computes, Java 22 reports without the preview flag:
Square.java:7: error: statements before super() is a preview feature and is disabled by default.
super(color, sideLength, sideLength);
^
(use --enable-preview to enable statements before super())
In Java 22, the feature is still called “statements before super()” here.
The fix is to raise the source level to 25: in Maven via the maven.compiler.release property, in Gradle via a Java toolchain with language version 25, in IntelliJ via File→Project Structure→Project Settings→Project→Project language level, and in Eclipse via Project→Properties→Java Compiler→Compiler compliance level. Visual Studio Code takes the level from the Maven or Gradle build file.
If your project still runs on Java 23 or 24, you can compile the code with --enable-preview --release <version> – on Java 22 only code like the Square example, which sets no fields before super(...). You then have to start the program with --enable-preview as well. Without that flag, the class loader rejects the class:
Exception in thread "main" java.lang.UnsupportedClassVersionError: Preview features are not enabled for Rectangle (class file version 68.65535). Try running with '--enable-preview'
The 68 stands for Java 24. Classes compiled with Java 23 report 67, those compiled with Java 22 report 66.
History
Flexible Constructor Bodies were defined in the following JDK Enhancement Proposals:
- Java 22: JEP 447: Statements before super(...) (Preview)
- Java 23: JEP 482: Flexible Constructor Bodies (Second Preview)
- Java 24: JEP 492: Flexible Constructor Bodies (Third Preview)
- Java 25: JEP 513: Flexible Constructor Bodies
Conclusion
Executing code before super(...) or this(...) allows fields to be initialized and parameters to be validated or calculated before the super constructor or an alternative constructor is called. That makes the code safer and enables much more expressive code than the workarounds we had to construct up to Java 24.
Flexible Constructor Bodies were finalized in Java 25 and have been available without a preview flag ever since. If you’re still on Java 23 or 24, you can enable the feature there as a preview using --enable-preview --release <version>. Java 22’s preview only allows statements before super(...), not yet assignments to fields.
What should I cover next? The best way for me to find out is through your feedback: a review on my ProvenExpert profile shows me which topics matter to you – and motivates me to write more articles.




