Menu
Coddy logo textTech

Sealed Classes (Java 17+)

Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 52 von 87.

Im traditionellen Java kann jede andere Klasse eine von Ihnen erstellte Klasse erweitern (es sei denn, Sie markieren sie als final). Aber was ist, wenn Sie Vererbung zulassen möchten, aber nicht für jeden? Java 17 hat sealed classes eingeführt, um Ihnen eine präzise Kontrolle darüber zu geben, welche Klassen Ihre erweitern dürfen.

Eine versiegelte Klasse deklariert ihre zulässigen Unterklassen explizit unter Verwendung der Schlüsselwörter sealed und permits:

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    double radius;
    double area() { return Math.PI * radius * radius; }
}

final class Rectangle extends Shape {
    double width, height;
    double area() { return width * height; }
}

final class Triangle extends Shape {
    double base, height;
    double area() { return 0.5 * base * height; }
}

Nur Circle, Rectangle und Triangle können Shape erweitern. Jede andere Klasse, die versucht, sie zu erweitern, wird einen Kompilierungsfehler verursachen.

Jede zulässige Unterklasse muss einen von drei Modifikatoren verwenden: final (keine weitere Erweiterung), sealed (setzt die Einschränkungskette fort) oder non-sealed (öffnet für uneingeschränkte Erweiterung):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Kann nicht erweitert werden
non-sealed class Truck extends Vehicle { }    // Jeder kann Truck erweitern

Sealed Classes sind besonders leistungsfähig in Verbindung mit Pattern Matching in Switch-Ausdrücken, da der Compiler alle möglichen Subtypen kennt und die Vollständigkeit überprüfen kann. Sie sind ideal für die Modellierung einer festen Menge von Typen innerhalb einer Domäne, wie zum Beispiel Zahlungsmethoden, Antworttypen oder geometrische Formen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Benachrichtigungssystem bauen, das versiegelte Klassen (sealed classes) demonstriert, indem es einschränkt, welche Arten von Benachrichtigungen in Ihrer Anwendung existieren können. Sie werden eine Hierarchie erstellen, in der nur bestimmte Benachrichtigungstypen zulässig sind und jeder Typ die Zustellung unterschiedlich handhabt.

Sie werden Ihren Code in vier Dateien organisieren:

  • Notification.java: Erstellen Sie eine sealed abstrakte Klasse, die als Basis für alle Benachrichtigungen dient. Sie sollte genau drei Unterklassen zulassen: EmailNotification, SMSNotification und PushNotification. Ihre versiegelte Klasse benötigt ein geschütztes Feld message (String), einen Konstruktor zu dessen Initialisierung, einen Getter für die Nachricht und eine abstrakte Methode deliver(), die einen String zurückgibt, der beschreibt, wie die Benachrichtigung gesendet wird.
  • EmailNotification.java: Erstellen Sie eine final Klasse, die Notification erweitert. Eine EmailNotification hat ein zusätzliches privates Feld recipient (String) for the email address. Ihr Konstruktor sollte sowohl die Nachricht als auch den Empfänger entgegennehmen und den Konstruktor der Elternklasse mit super() aufrufen. Die Methode deliver() sollte Folgendes zurückgeben: Sending email to [recipient]: [message]
  • SMSNotification.java: Erstellen Sie eine non-sealed Klasse, die Notification erweitert – dies ermöglicht es anderen Klassen, sie bei Bedarf in Zukunft zu erweitern. Eine SMSNotification hat ein zusätzliches privates Feld phoneNumber (String). Ihr Konstruktor nimmt die Nachricht und die Telefonnummer entgegen. Die Methode deliver() sollte Folgendes zurückgeben: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: Erstellen Sie eine final Klasse, die Notification erweitert. Eine PushNotification hat ein zusätzliches privates Feld deviceId (String). Ihr Konstruktor nimmt die Nachricht und die Geräte-ID entgegen. Die Methode deliver() sollte Folgendes zurückgeben: Sending push to device [deviceId]: [message]
  • Main.java: Bringen Sie Ihr Benachrichtigungssystem zusammen! Sie erhalten vier Eingaben: eine Nachricht (String), eine E-Mail-Adresse (String), eine Telefonnummer (String) und eine Geräte-ID (String).

    Erstellen Sie von jedem Benachrichtigungstyp ein Exemplar unter Verwendung derselben Nachricht, aber der jeweiligen Kontaktinformationen. Speichern Sie alle drei in einem Array vom Typ Notification[], um Polymorphismus mit versiegelten Klassen zu demonstrieren. Iterieren Sie dann durch das Array und geben Sie das Ergebnis des Aufrufs von deliver() für jede Benachrichtigung aus.

Sie erhalten vier Eingaben in der folgenden Reihenfolge: Nachricht, E-Mail-Adresse, Telefonnummer und Geräte-ID.

Beachten Sie, wie die versiegelte Klasse die Hierarchie einschränkt – nur die drei zulässigen Klassen können Notification erweitern. Jede Unterklasse muss sich selbst als final, sealed oder non-sealed deklarieren. Dies gibt Ihnen die volle Kontrolle über Ihre Typhierarchie und ermöglicht dennoch Polymorphismus!

Spickzettel

Eine versiegelte Klasse (sealed class) schränkt mithilfe der Schlüsselwörter sealed und permits ein, welche Klassen sie erweitern können:

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

Nur die in permits aufgeführten Klassen können die versiegelte Klasse erweitern. Jede andere Klasse, die versucht, sie zu erweitern, führt zu einem Kompilierungsfehler.

Jede zulässige Unterklasse muss einen von drei Modifikatoren verwenden:

  • final – verhindert weitere Erweiterungen
  • sealed – setzt die Einschränkungskette mit eigenen zulässigen Unterklassen fort
  • non-sealed – öffnet die Hierarchie für uneingeschränkte Erweiterungen
sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Kann nicht erweitert werden
non-sealed class Truck extends Vehicle { }    // Jeder kann Truck erweitern

Versiegelte Klassen sind nützlich für die Modellierung einer festen Gruppe von Typen in einer Domäne, wie z. B. Zahlungsmethoden, Antworttypen oder geometrische Formen. Sie funktionieren gut mit Pattern Matching in Switch-Ausdrücken, da der Compiler alle möglichen Untertypen kennt.

Probier es selbst

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Lies die vier Eingaben
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: Erstelle von jedem Benachrichtigungstyp eine Instanz mit derselben message
        // - EmailNotification mit message und email
        // - SMSNotification mit message und phoneNumber
        // - PushNotification mit message und deviceId
        
        // TODO: Speichere alle drei Benachrichtigungen in einem Notification[] Array
        // Dies demonstriert Polymorphismus mit sealed classes
        
        // TODO: Iteriere durch das Array und gib das Ergebnis von deliver() aus
        // für jede Benachrichtigung
        
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung