Menu
Coddy logo textTech

Sealed Classes (Java 17+)

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 52 of 87.

In traditional Java, when you create a class, any other class can extend it (unless you mark it final). But what if you want to allow inheritance, just not from anyone? Java 17 introduced sealed classes to give you precise control over which classes can extend yours.

A sealed class explicitly declares its permitted subclasses using the sealed and permits keywords:

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; }
}

Only Circle, Rectangle, and Triangle can extend Shape. Any other class attempting to extend it will cause a compilation error.

Each permitted subclass must use one of three modifiers: final (no further extension), sealed (continues the restriction chain), or non-sealed (opens up for unrestricted extension):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Cannot be extended
non-sealed class Truck extends Vehicle { }    // Anyone can extend Truck

Sealed classes are particularly powerful with pattern matching in switch expressions, as the compiler knows all possible subtypes and can verify exhaustiveness. They're ideal when modeling a fixed set of types in a domain, like payment methods, response types, or geometric shapes.

challenge icon

Challenge

Easy

Let's build a notification system that demonstrates sealed classes by restricting which types of notifications can exist in your application. You'll create a hierarchy where only specific notification types are permitted, and each type handles delivery differently.

You'll organize your code across four files:

  • Notification.java: Create a sealed abstract class that serves as the base for all notifications. It should permit exactly three subclasses: EmailNotification, SMSNotification, and PushNotification. Your sealed class needs a protected field message (String), a constructor to initialize it, a getter for the message, and an abstract method deliver() that returns a String describing how the notification is sent.
  • EmailNotification.java: Create a final class that extends Notification. An EmailNotification has an additional private field recipient (String) for the email address. Its constructor should take both the message and recipient, calling the parent constructor with super(). The deliver() method should return: Sending email to [recipient]: [message]
  • SMSNotification.java: Create a non-sealed class that extends Notification—this allows other classes to extend it if needed in the future. An SMSNotification has an additional private field phoneNumber (String). Its constructor takes the message and phone number. The deliver() method should return: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: Create a final class that extends Notification. A PushNotification has an additional private field deviceId (String). Its constructor takes the message and device ID. The deliver() method should return: Sending push to device [deviceId]: [message]
  • Main.java: Bring your notification system together! You'll receive four inputs: a message (String), an email address (String), a phone number (String), and a device ID (String).

    Create one of each notification type using the same message but their respective contact information. Store all three in an array of type Notification[] to demonstrate polymorphism with sealed classes. Then iterate through the array and print the result of calling deliver() on each notification.

You will receive four inputs in order: message, email address, phone number, and device ID.

Notice how the sealed class restricts the hierarchy—only the three permitted classes can extend Notification. Each subclass must declare itself as final, sealed, or non-sealed. This gives you complete control over your type hierarchy while still enabling polymorphism!

Cheat sheet

A sealed class restricts which classes can extend it using the sealed and permits keywords:

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

Only the classes listed in permits can extend the sealed class. Any other class attempting to extend it will cause a compilation error.

Each permitted subclass must use one of three modifiers:

  • final - prevents further extension
  • sealed - continues the restriction chain with its own permitted subclasses
  • non-sealed - opens the hierarchy for unrestricted extension
sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Cannot be extended
non-sealed class Truck extends Vehicle { }    // Anyone can extend Truck

Sealed classes are useful for modeling a fixed set of types in a domain, such as payment methods, response types, or geometric shapes. They work well with pattern matching in switch expressions, as the compiler knows all possible subtypes.

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Read the four inputs
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: Create one of each notification type using the same message
        // - EmailNotification with message and email
        // - SMSNotification with message and phoneNumber
        // - PushNotification with message and deviceId
        
        // TODO: Store all three notifications in a Notification[] array
        // This demonstrates polymorphism with sealed classes
        
        // TODO: Iterate through the array and print the result of deliver()
        // for each notification
        
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming