Sealed Classes mit Java JDK 17
Was sind Sealed Classes?
Mit dem kommenden Java Release 17 werden sogenannte Sealed Classes eingeführt. Diese waren zuvor bereits seit dem JDK 15 in Form eines Preview Features vorhanden und in JDK 16 überarbeitet. Nun erscheinen sie final mit JDK 17.
Bei diesem Feature wird es möglich sein, das Erben und Implementieren von Klassen bzw. Interfaces auf bestimmte andere Klassen und Interfaces einzuschränken. Kurz gesagt, die entwickelnde Person kann vorgeben, welche andere Klasse erben darf.
Ziele und Nutzen
Sealed Classes ermöglichen eine gezielte Festlegung durch die erstellende Person, welcher Code für die Implementierung zuständig ist. Restriktionen von Superklassen waren zuvor nur durch Access Modifier möglich. Jetzt ergeben sich weitere Möglichkeiten.Gerade bei API Entwicklungen gibt es hier aus meiner Sicht einen hohen Nutzen. Bisher mussten Klassen als final deklariert werden, um Subklassen ganz zu verbieten oder der Constructor benötigte den private Modifier um Subklassen im selben Package zu verwenden. Sobald es aber darum ginge alternativen zu entwickeln konnte dieser Code nicht mehr verwendet werden, da er sich außerhalb des Packages befand. Es war bisher nicht ohne große Aufwände möglich eine Superklasse zu verwenden, ohne von ihr zu erben.
Codebeschreibung
Mit dem Modifier "sealed" wird die Klasse als solche deklariert. Mit dem Schlüsselwort "permits" können im Nachgang die zugelassenen Klassen eingefügt werden.
Beispiele:
public abstract sealed class Fahrzeug
permits com.example.test.Lkw, Pkw, Fahrrad { ... }
Es ist auch möglich die erlaubten Klassen direkt im Sourcefile zu erstellen. Das Schlüsselwort "permits" wird hier weggelassen. Der Javacompiler leitet die erlaubten Klassen anhand des Codes ab.
abstract sealed class Root { ...
final class A extends Root { ... }
final class B extends Root { ... }
final class C extends Root { ... }
}
Zu beachten
- Die Sealed Class und die erlaubten Unterklassen müssen sich im gleichen Modul befinden.
Eine Besonderheit gibt es noch für "unnamed module". Diese müssen sich im gleichen Package befinden. - Jede erlaubte Unterklasse muss von der Sealed Class erben.
- Jede erlaubte Unterklasse muss entweder final, sealed oder non-sealed deklariert sein.
Beispiel
Angenommen wir haben eine Klasse Fahrzeug. Von dieser Klasse erbt PKW, LKW und anschließend Fahrrad.
public abstract sealed class Fahrzeug
permits Pkw, Lkw, Fahrrad { /*some code*/ }
public final class Pkw extends Fahrzeug { ... }
public sealed class Lkw extends Fahrzeug
permits Kleinlaster, Schwertransport { ... }
public final class Kleinlaster extends Lkw { ... }
public final class Schwertransport extends Lkw { ... }
public non-sealed class Fahrrad extends Fahrzeug { ... }
Die Klasse Fahrrad ist somit offen für Erweiterungen aber geschlossen für Änderungen (Open-Closed Prinzip). Es ist nicht möglich, final und sealed zu sein, nur ein Modifier ist erlaubt. Interfaces verhalten sich Analog zu Klassen.
Fazit
Ich persönlich sehe hier großes Potential die API-Entwicklung zu vereinfachen, besonders im Bezug auf Security Aspekte. Sobald eine Sealed Vorraussetzung verletzt wird, wirft der Compiler eine Exception, das hilft nicht nur beim Entwickeln, sondern auch beim Testen. Unvorhersehbare Effekte werden dadurch verringert. Ich bin auf jeden Fall sehr gespannt, wenn dieses Feature fertig released wird und welche neuen Möglichkeiten sich daraus ergeben können.