GEDOPLAN
JavaIT-TrainingSoftwarequalität und Performanz

Flexible Constructor Bodies in Java 25

JavaIT-TrainingSoftwarequalität und Performanz
Zwei gelenkige Holzpuppen schütteln sich die Hand

Erstmals eingeführt mit Java 22, dann mit Java 23 und Java 24 fortgeführt, werden Flexible Constructor Bodies mit Java 25 nun finalisiert und offiziell ausgeliefert. Sie helfen, unnötig komplexen Code zu vermeiden und den Code lesbarer und verständlicher zu gestalten.

TL;DR

In Konstruktoren dürfen Anweisungen nun auch vor dem Aufruf des Konstruktors der Oberklasse mittels super() oder eines anderen Konstruktors derselben Klasse mittels this() stehen. Das ermöglicht eine bessere Strukturierung des Codes und eine frühere Validierung von Parametern.

Problem

Schauen wir uns ein einfaches Bespiel an. Eine Basisklasse Vehicle hat einen name und einen price. Der Name sollte weder null noch leer sein, und der Preis eine positive Zahl.

public abstract class Vehicle {  
	String name;  
	BigDecimal price;  

	public Vehicle(String name, BigDecimal price) {  
	    if (name == null || name.isBlank()) {  
	      throw new IllegalArgumentException("name must not be null or blank");  
	    }  
	    if (price == null) {  
	      throw new IllegalArgumentException("price must not be null");  
	    }  
	    if (price.signum() < 0) {  
	      throw new IllegalArgumentException("price must be >= 0");  
	    }  
	    this.name = name;  
	    this.price = price;  
	}
  ...
}

Die Konstruktorparameter werden überprüft, bei Nichtgefallen gibt es eine Exception. So weit, so gut.

Eine Klasse Car erweitert Vehicle und hat zusätzlich eine numberOfSeats und eine numberOfDoors. Auch diese müssen natürlich bei der Objekterzeugung validiert werden, wobei name und price bequem an den Oberklassenkonstruktor durchgereicht werden können:

public CarJdk21(String name, BigDecimal price,
				int numberOfSeats, int numberOfDoors) {
				
  // zuerst Aufruf des Oberklassenkonstruktors
  super(name, price);

  // erst danach Validierung der Parameter
  if (numberOfSeats < 1) {  
    throw new IllegalArgumentException("numberOfSeats must be >= 1");  
  }  
  if (numberOfDoors < 1) {  
    throw new IllegalArgumentException("numberOfDoors must be >= 1");  
  }  

  // Zuweisung an die Instanzvariablen
  this.numberOfSeats = numberOfSeats;  
  this.numberOfDoors = numberOfDoors;  
}

Nach sorgfältiger Betrachtung des Codes fällt auf: Noch vor der Validierung von numberOfSeats und numberOfDoors wird der Konstruktor von Vehicle aufgerufen, führt diverse Funktionalitäten aus, und belegt am Ende (wenn bis dahin alles geklappt hat) die Attribute name und price mit ihren Werten, obwohl noch gar nicht klar ist, ob die weiteren Attribute überhaupt gültig sind.

Leider geht das bisher gar nicht anders, denn in der Java Language Specification (JLS) steht in § 8.8.7. Constructor Body:

Die erste Anweisung eines Konstruktors kann ein expliziter Aufruf eines anderen Konstruktors derselben Klasse oder der direkten Superklasse sein.

Ein Konstruktor kann beliebigen Code enthalten, ein Aufruf von this() und super() ist aber nur in der ersten Zeile erlaubt.

Auftritt: „Flexible Constructor Bodies“.

Wenn ein Konstruktor einen expliziten Konstruktoraufruf enthält, werden die Statements vor dem expliziten Konstruktoraufruf als Prolog des Konstruktorkörpers bezeichnet.

Oder einfacher formuliert: Vor dem Aufruf von this()oder super() dürfen jetzt Anweisungen stehen.

In diesem Prolog gelten besondere Regeln.

Erlaubt sind:

  • Aufruf von statischen Methoden,
  • Zugriff auf Klassenvariablen

Unzulässig sind:

  • Referenzen auf das aktuelle Objekt mit thisoder super,
  • Zugriff auf Instanzvariablen oder Instanzmethoden

Folgendes führt also zu Compilerfehlern:

class Pet {
	String name;
	Pet(String name) { this.name = name; }
}

class Feeder {
	static List<Pet> pets;
	static void register(Pet pet) { this.pets.add(pet);} 
}

class ColouredPet {
	// Instanzvariable
	String colour;
	
	Pet(String name) {
		// vor dem Aufruf des Konstruktors der Oberklasse
		this.colour = "blue";                // <=== Fehler: Attributzugriff
		String cuteName = cuteify(cuteName); // <=== Fehler: Methodenzugriff
		Feeder.register(this);               // <=== Fehler: Refererenz auf this
		
		// Konstruktor der Oberklasse
		super(name);
	}
	
	// Instanzmethode
	String cuteify(String name) {
		return name + "y";
	} 
}

Anwendung

Nach diesen Betrachtungen wenden wir uns unserem einführenden Beispiel zu.

Unsere Klasse Car können wir nun so umformulieren:

public CarJdk25(String name, BigDecimal price,
				int numberOfSeats, int numberOfDoors) {
  // Validierung
  if (numberOfSeats < 1) {  
    throw new IllegalArgumentException("numberOfSeats must be >= 1");  
  }  
  if (numberOfDoors < 1) {  
    throw new IllegalArgumentException("numberOfDoors must be >= 1");  
  }
  
  //  Aufruf des Oberklassenkonstruktors
  super(name, price);

  // initialisierung der Instanzvariablen
  this.numberOfSeats = numberOfSeats;  
  this.numberOfDoors = numberOfDoors;  
}

Der Vorteil ist deutlich zu sehen:

  • Der alte Weg ():CarJdk21 Der Aufruf von super(name, price) muss die erste Anweisung sein. Die Validierung der zusätzlichen Felder (numberOfSeats, numberOfDoors) kann erst danach erfolgen. Schlägt diese Validierung fehl, wurde das Vehicle-Objekt bereits unnötigerweise initialisiert.
  • Der neue Weg ():CarJdk25 Die Validierung von numberOfSeats und numberOfDoors kann vor dem super()-Aufruf stattfinden. Dies verhindert die unnötige Arbeit im Super-Konstruktor, falls die Parameter der Unterklasse ungültig sind, und macht den Kontrollfluss logischer.

Obwohl sich der Code in Vehicle selbst kaum ändert, ermöglicht die neue Java-Version einen deutlich saubereren und effizienteren Aufbau der Konstruktoren in den davon abgeleiteten Klassen.

Ergebnis

Die Struktur des Codes kann jetzt deutlich besser dem tatsächlich gewünschten Ablauf entsprechen, sowohl bei Weiterleitung von einem Konstruktor zum anderen innerhalb einer Klasse als auch beim Aufruf des Konstruktors der Oberklasse. Der Code wird verständlicher, besser nachvollziehbar und damit auch besser wartbar.

Fazit

Java ist toll! Mit der Einführung von „Flexible Constructor Bodies“ wird die Sprache noch flexibler und benutzerfreundlicher. Diese neue Funktion ermöglicht es Entwicklern, ihren Code besser zu strukturieren und zu validieren, was sowohl zu einer höheren Codequalität als auch einer besseren Wartbarkeit führt.

Weitere Informationen

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

bumper cars 4390958 640
Webprogrammierung

Angular Material Theming

Angular als Framework für die Entwicklung von anspruchsvollen Webanwendungen bringt alles mit was der Entwickler braucht. Alles? Nicht ganz. Ähnlich…
IT-Training - GEDOPLAN
Entwicklungswerkzeuge

Upgrade auf Maven 3

Wir benutzen für unsere Projekte schon seit Langem Maven als Build-Werkzeug. Nachdem die neue Version 3 schon seit ein paar…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!