1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Sealed classes (Java 17+) = controlled inheritance—restrict which classes can extend/implement. sealed class Shape permits Circle, Square = only permitted subclasses allowed.
- Subclasses must be: final (no further extension), sealed (continue restriction), non-sealed (open inheritance).
- Benefits: Exhaustive switch (compiler knows all types), domain modeling (algebraic data types), API control (prevent unauthorized extensions).
- Use for: Payment types, user roles, state machines!
Think of exclusive club. Normal class = open to public (anyone can join). Sealed class = members-only (permits list = approved members). Subclass options: final = joined but can't sponsor others, sealed = can sponsor specific people, non-sealed = can sponsor anyone. Bouncer enforces list!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Sealed class: VIP section of club (controlled access)
- permits: Guest list (who's allowed in)
- final subclass: Guest (came in, but can't bring friends)
- sealed subclass: Member (can bring specific approved guests)
- non-sealed: Staff (can bring anyone)
Sealed Class Hierarchy
graph TD
A[sealed Shape<br/>permits Circle, Square, Triangle] --> B[final Circle]
A --> C[final Square]
A --> D[sealed Triangle<br/>permits RightTriangle]
D --> E[final RightTriangle]
style A fill:#C2185B
style B fill:#2E7D32
style C fill:#2E7D32
style D fill:#C2185B
style E fill:#2E7D32
3. Technical Mastery (The "Deep Dive")
Sealed Class Rules
| Rule | Description |
|---|---|
| sealed modifier | Declares class sealed |
| permits clause | Lists allowed subclasses |
| Same package/module | Permitted classes must be accessible |
| Subclass modifiers | Must be final, sealed, or non-sealed |
4. Interactive & Applied Code
java
// ========================================
// EXAMPLE 1: Basic Sealed Class
// ========================================
sealed class Shape permits Circle, Square, Triangle {}
final class Circle extends Shape {
double radius;
Circle(double radius) { this.radius = radius; }
double area() { return Math.PI * radius * radius; }
}
final class Square extends Shape {
double side;
Square(double side) { this.side = side; }
double area() { return side * side; }
}
final class Triangle extends Shape {
double base, height;
Triangle(double base, double height) {
this.base = base;
this.height = height;
}
double area() { return 0.5 * base * height; }
}
// ❌ COMPILE ERROR: Not in permits list
// class Pentagon extends Shape {}
public class SealedClassesDemo {
public static void main(String[] args) {
demonstrateExhaustiveSwitch();
demonstratePaymentExample();
demonstrateUserRoles();
}
// Exhaustive switch (pattern matching)
static void demonstrateExhaustiveSwitch() {
System.out.println("=== EXHAUSTIVE SWITCH ===");
Shape shape = new Circle(5);
// ✅ Compiler knows ALL possible types
double area = switch (shape) {
case Circle c -> c.area();
case Square s -> s.area();
case Triangle t -> t.area();
// No default needed! Compiler knows these are ALL types
};
System.out.println("Area: " + area);
}
// Real-world: Payment types
sealed interface Payment permits CreditCard, DebitCard, Cash {}
record CreditCard(String number, String cvv) implements Payment {}
record DebitCard(String number, String pin) implements Payment {}
record Cash(double amount) implements Payment {}
static void demonstratePaymentExample() {
System.out.println("\n=== PAYMENT EXAMPLE ===");
Payment payment = new CreditCard("1234-5678", "123");
String message = switch (payment) {
case CreditCard cc -> "Processing credit card: " + cc.number();
case DebitCard dc -> "Processing debit card: " + dc.number();
case Cash cash -> "Received cash: $" + cash.amount();
};
System.out.println(message);
}
// Sealed hierarchy with non-sealed
sealed class UserRole permits Admin, User {}
final class Admin extends UserRole {
void manageUsers() {
System.out.println("Managing users");
}
}
non-sealed class User extends UserRole {
// non-sealed: Anyone can extend User now
void viewContent() {
System.out.println("Viewing content");
}
}
// ✅ OK: User is non-sealed
class PremiumUser extends User {
void accessPremium() {
System.out.println("Accessing premium content");
}
}
static void demonstrateUserRoles() {
System.out.println("\n=== USER ROLES ===");
UserRole role = new Admin();
switch (role) {
case Admin admin -> admin.manageUsers();
case User user -> user.viewContent();
}
}
}
// ========================================
// EXAMPLE: Result Type (Success/Failure)
// ========================================
sealed interface Result<T> permits Success, Failure {}
record Success<T>(T value) implements Result<T> {}
record Failure<T>(String error) implements Result<T> {}
class ResultDemo {
public static void main(String[] args) {
Result<Integer> result = divide(10, 2);
String message = switch (result) {
case Success<Integer> s -> "Result: " + s.value();
case Failure<Integer> f -> "Error: " + f.error();
};
System.out.println(message);
}
static Result<Integer> divide(int a, int b) {
if (b == 0) {
return new Failure<>("Division by zero");
}
return new Success<>(a / b);
}
}
// ========================================
// EXAMPLE: State Machine
// ========================================
sealed interface OrderState permits Pending, Confirmed, Shipped, Delivered {}
record Pending() implements OrderState {}
record Confirmed(String confirmationCode) implements OrderState {}
record Shipped(String trackingNumber) implements OrderState {}
record Delivered(java.time.LocalDateTime deliveryTime) implements OrderState {}
class Order {
private OrderState state = new Pending();
void confirm(String code) {
state = new Confirmed(code);
}
void ship(String tracking) {
state = new Shipped(tracking);
}
String getStatus() {
return switch (state) {
case Pending p -> "Order pending";
case Confirmed c -> "Confirmed: " + c.confirmationCode();
case Shipped s -> "Shipped: " + s.trackingNumber();
case Delivered d -> "Delivered at: " + d.deliveryTime();
};
}
}5. The Comparison & Decision Layer
| Feature | Normal Class | Sealed Class |
|---|---|---|
| Inheritance | Open to all | Restricted to permits |
| Exhaustiveness | default required | No default needed |
| Domain modeling | Leaky abstraction | Precise control |
| Extension | Unpredictable | Predictable |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "final vs sealed vs non-sealed subclass?" Answer:
- final: End of the line (no further inheritance)
- sealed: Continues restriction (permits own list)
- non-sealed: Opens inheritance (anyone can extend)
java
sealed class Animal permits Dog, Cat {}
final class Dog extends Animal {}
// ✅ final: Can't extend Dog
sealed class Cat extends Animal permits Kitten {}
// ✅ sealed: Can extend Cat, but only Kitten
non-sealed class Bird extends Animal {}
// ✅ non-sealed: Anyone can extend Bird
class Sparrow extends Bird {} // ✅ OK: Bird is non-sealedPro-Tips:
- Sealed + Records = Algebraic Data Types:
java
sealed interface Expr permits Num, Add, Mul {}
record Num(int value) implements Expr {}
record Add(Expr left, Expr right) implements Expr {}
record Mul(Expr left, Expr right) implements Expr {}
// Exhaustive pattern matching
int eval(Expr expr) {
return switch (expr) {
case Num(int value) -> value;
case Add(var l, var r) -> eval(l) + eval(r);
case Mul(var l, var r) -> eval(l) * eval(r);
};
}- When to use sealed:
java
// ✅ USE: Finite set of types (payment methods, states)
sealed interface Payment permits Cash, Card {}
// ❌ DON'T USE: Open-ended extension (plugins, frameworks)
// Not sealed: class Plugin {} // Let users extend!