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 TruckSealed 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
EasyLet'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, andPushNotification. Your sealed class needs a protected fieldmessage(String), a constructor to initialize it, a getter for the message, and an abstract methoddeliver()that returns a String describing how the notification is sent.EmailNotification.java: Create afinalclass that extends Notification. An EmailNotification has an additional private fieldrecipient(String) for the email address. Its constructor should take both the message and recipient, calling the parent constructor withsuper(). Thedeliver()method should return:Sending email to [recipient]: [message]SMSNotification.java: Create anon-sealedclass that extends Notification—this allows other classes to extend it if needed in the future. An SMSNotification has an additional private fieldphoneNumber(String). Its constructor takes the message and phone number. Thedeliver()method should return:Sending SMS to [phoneNumber]: [message]PushNotification.java: Create afinalclass that extends Notification. A PushNotification has an additional private fielddeviceId(String). Its constructor takes the message and device ID. Thedeliver()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 callingdeliver()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 extensionsealed- continues the restriction chain with its own permitted subclassesnon-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 TruckSealed 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
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsFields (Attributes)Constructor MethodConstructor OverloadingRecap - Simple Calculator4Inheritance
Basic Inheritance (extends)The super KeywordMethod Overriding (@Override)Constructor ChainingThe Object ClassSingle & Multilevel InheritWhy No Multi Class InheritRecap - Employee Hierarchy7Special Methods & Object Class
toString() Methodequals() and hashCode()clone() MethodcompareTo() and ComparableComparator InterfaceRecap - Custom Sorting2Access Modifiers & Encapsulate
Access Levels OverviewGetter and Setter MethodsInformation HidingThe final KeywordRecap - Bank Account Manager5Polymorphism
Method Overloading BasicsMethod Overriding (Run-Time)Upcasting and DowncastingThe instanceof OperatorAbstract Classes and MethodsRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceAggregation vs CompositionInner Nested & Anonymous ClassEnums and Enum MethodsRecords (Java 16+)Sealed Classes (Java 17+)3Class Props & Static Member
Instance vs Static VariablesStatic MethodsStatic BlocksConstants (static final)Recap - Counter & Utility6Interfaces & Abstract Classes
Introduction to InterfacesImplementing InterfacesMulti Interface ImplemenDefault & Static in InterfaceAbstract Classes vs InterfacesFunctional InterfacesRecap - Payment System