Versiegelte Klassen in Kotlin

1. Einleitung

Einfach ausgedrückt, die Kotlin-Sprache hat eine Reihe von Konzepten aus anderen funktionalen Sprachen übernommen, um sichereren und besser lesbaren Code zu schreiben. Versiegelte Hierarchien sind eines dieser Konzepte.

2. Was ist eine versiegelte Klasse?

Mit versiegelten Klassen können wir Typhierarchien festlegen und Entwicklern das Erstellen neuer Unterklassen untersagen.

Sie sind nützlich, wenn Sie eine sehr strenge Vererbungshierarchie mit einem bestimmten Satz möglicher Unterklassen und keinen anderen haben. Der Compiler garantiert, dass nur Klassen, die in derselben Quelldatei wie die versiegelte Klasse definiert sind, von dieser erben können.

Versiegelte Klassen sind auch implizit abstrakt . Sie sollten im gesamten Rest Ihres Codes als solche behandelt werden, außer dass nichts anderes sie implementieren kann.

In versiegelten Klassen können Felder und Methoden definiert sein, einschließlich abstrakter und implementierter Funktionen. Dies bedeutet, dass Sie eine Basisdarstellung der Klasse haben und diese dann an die Unterklassen anpassen können.

3. Wann werden versiegelte Klassen verwendet?

Versiegelte Klassen sind so konzipiert, dass sie verwendet werden, wenn es einen bestimmten Satz möglicher Optionen für einen Wert gibt und jede dieser Optionen funktional unterschiedlich ist - nur algebraische Datentypen.

Häufige Anwendungsfälle können die Implementierung einer Zustandsmaschine oder die monadische Programmierung sein, die mit dem Aufkommen funktionaler Programmierkonzepte immer beliebter wird.

Immer wenn Sie mehrere Optionen haben und diese sich nur in der Bedeutung der Daten unterscheiden, ist es möglicherweise besser, stattdessen Enum-Klassen zu verwenden.

Immer wenn Sie eine unbekannte Anzahl von Optionen haben, können Sie keine versiegelte Klasse verwenden, da Sie dadurch keine Optionen außerhalb der ursprünglichen Quelldatei hinzufügen können.

4. Versiegelte Klassen schreiben

Beginnen wir mit dem Schreiben unserer eigenen versiegelten Klasse - das gute Beispiel für eine solche versiegelte Hierarchie ist eine Option aus Java 8 - die entweder Some oder None sein kann.

Bei der Implementierung ist es sehr sinnvoll, die Möglichkeit der Erstellung neuer Implementierungen einzuschränken. Die beiden bereitgestellten Implementierungen sind erschöpfend und niemand sollte seine eigenen hinzufügen.

Als solches können wir dies implementieren:

sealed class Optional { // ... abstract fun isPresent(): Boolean } data class Some(val value: V) : Optional() { // ... override fun isPresent(): Boolean = true } class None : Optional() { // ... override fun isPresent(): Boolean = false }

Es kann jetzt garantiert werden, dass Sie jedes Mal, wenn Sie eine Instanz von Optional haben, entweder ein Some oder ein None haben.

In Java 8 sieht die tatsächliche Implementierung anders aus, da keine versiegelten Klassen vorhanden sind.

Wir können dies dann in unseren Berechnungen verwenden:

val result: Optional = divide(1, 0) println(result.isPresent()) if (result is Some) { println(result.value) }

Die erste Zeile gibt entweder ein Some oder ein None zurück . Wir geben dann aus, ob wir ein Ergebnis erhalten haben oder nicht.

5. Verwenden Sie mit Wann

Kotlin unterstützt die Verwendung versiegelter Klassen in seinen when- Konstrukten. Da es immer einen genauen Satz möglicher Unterklassen gibt, kann der Compiler Sie warnen, wenn ein Zweig nicht behandelt wird, genauso wie dies bei Aufzählungen der Fall ist.

Dies bedeutet, dass in solchen Situationen normalerweise kein Catch-All-Handler erforderlich ist, was wiederum bedeutet, dass das Hinzufügen einer neuen Unterklasse automatisch sicher ist. Der Compiler warnt Sie sofort, wenn Sie dies nicht getan haben, und Sie benötigen um solche Fehler zu beheben, bevor Sie fortfahren.

Das obige Beispiel kann erweitert werden, um je nach zurückgegebenem Typ entweder das Ergebnis oder einen Fehler auszugeben:

val message = when (result) { is Some -> "Answer: ${result.value}" is None -> "No result" } println(message)

Wenn einer der beiden Zweige fehlt, wird dies nicht kompiliert und führt stattdessen zu einem Fehler von:

'when' expression must be exhaustive, add necessary 'else' branch

6. Zusammenfassung

Versiegelte Klassen können ein unschätzbares Werkzeug für Ihre API-Design-Toolbox sein. Wenn Sie eine bekannte, strukturierte Klassenhierarchie zulassen, die immer nur zu einer erwarteten Gruppe von Klassen gehören kann, können Sie eine ganze Reihe potenzieller Fehlerbedingungen aus Ihrem Code entfernen und gleichzeitig das Lesen und Verwalten vereinfachen.

Wie immer finden Sie Code-Schnipsel auf GitHub.