Your Result type should only be Success or Failure - nothing else. Sealed classes restrict which classes can extend them. The compiler knows all possibilities, enabling exhaustive pattern matching in switch expressions.

Basic sealed class

Restrict which classes can extend.

SealedBasics.java
// Basic Sealed Class Syntax

// Sealed class - restricts who can extend
sealed class Animal permits Dog, Cat, Bird {
    private String name;

    Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void describe() {
        System.out.println("I am " + name);
    }
}

// Permitted subclass - must be final, sealed, or non-sealed
final class Dog extends Animal {
    Dog(String name) {
        super(name);
    }

    public void bark() {
        System.out.println(getName() + " says: Woof!");
    }
}

final class Cat extends Animal {
    Cat(String name) {
        super(name);
    }

    public void meow() {
        System.out.println(getName() + " says: Meow!");
    }
}

final class Bird extends Animal {
    Bird(String name) {
        super(name);
    }

    public void chirp() {
        System.out.println(getName() + " says: Chirp!");
    }
}

// This would NOT compile!
// class Fish extends Animal { }  // Error: Fish is not in permits list

public class SealedBasics {
    public static void main(String[] args) {
        System.out.println("=== Sealed Classes Basics ===\n");

        // Create permitted subclasses
        Dog dog = new Dog("Buddy");
        Cat cat = new Cat("Whiskers");
        Bird bird = new Bird("Tweety");

        dog.describe();
        dog.bark();

        System.out.println();
        cat.describe();
        cat.meow();

        System.out.println();
        bird.describe();
        bird.chirp();

        System.out.println("\n=== Polymorphism Works ===");

        Animal[] animals = {dog, cat, bird};
        for (Animal animal : animals) {
            animal.describe();
        }

        System.out.println("\n=== Why Sealed Classes? ===");
        System.out.println("""
            1. CONTROL: Know exactly which classes extend yours
            2. SAFETY: Prevent unexpected subclasses
            3. EXHAUSTIVE: Switch can cover all cases
            4. DOCUMENTATION: Permits list shows intent

            Without sealed:
            - Anyone can extend your class
            - Switch might miss cases
            - Library users could break assumptions

            With sealed:
            - Only permitted classes can extend
            - Compiler knows all subclasses
            - Pattern matching is exhaustive
            """);

        System.out.println("=== Sealed Class Rules ===");
        System.out.println("""
            1. Use 'sealed' modifier on class
            2. Use 'permits' to list allowed subclasses
            3. Each permitted class must:
               - Be in same module (or package if unnamed)
               - Directly extend the sealed class
               - Be 'final', 'sealed', or 'non-sealed'
            """);
    }
}

sealed class X permits A, B, C - only A, B, C can extend X.

sealed Restricts subclasses to explicitly permitted list.

Permitted subclass options

Subclasses must be final, sealed, or non-sealed.

PermitsOptions.java
// Three Options for Permitted Subclasses

// Sealed parent
sealed class Vehicle permits Car, Motorcycle, Truck {
    private String brand;

    Vehicle(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }
}

// Option 1: FINAL - cannot be extended
final class Motorcycle extends Vehicle {
    Motorcycle(String brand) {
        super(brand);
    }

    public void wheelie() {
        System.out.println(getBrand() + " motorcycle doing a wheelie!");
    }
}
// class SportBike extends Motorcycle { }  // ERROR! Motorcycle is final

// Option 2: SEALED - must specify own permits
sealed class Car extends Vehicle permits Sedan, SUV, SportsCar {
    Car(String brand) {
        super(brand);
    }

    public void honk() {
        System.out.println(getBrand() + " car: Beep beep!");
    }
}

// Car's permitted subclasses must also choose
final class Sedan extends Car {
    Sedan(String brand) {
        super(brand);
    }
}

final class SUV extends Car {
    SUV(String brand) {
        super(brand);
    }
}

final class SportsCar extends Car {
    SportsCar(String brand) {
        super(brand);
    }
}

// Option 3: NON-SEALED - open for anyone
non-sealed class Truck extends Vehicle {
    Truck(String brand) {
        super(brand);
    }

    public void loadCargo() {
        System.out.println(getBrand() + " truck loading cargo");
    }
}

// Anyone can extend non-sealed!
class PickupTruck extends Truck {
    PickupTruck(String brand) {
        super(brand);
    }
}

class SemiTruck extends Truck {
    SemiTruck(String brand) {
        super(brand);
    }
}

// Even more levels!
class MonsterTruck extends PickupTruck {
    MonsterTruck(String brand) {
        super(brand);
    }
}

public class PermitsOptions {
    public static void main(String[] args) {
        System.out.println("=== Three Options for Permitted Classes ===\n");

        System.out.println("1. FINAL (Motorcycle):");
        Motorcycle harley = new Motorcycle("Harley");
        harley.wheelie();
        // Cannot create subclass of Motorcycle

        System.out.println("\n2. SEALED (Car):");
        Sedan toyota = new Sedan("Toyota");
        SUV jeep = new SUV("Jeep");
        SportsCar porsche = new SportsCar("Porsche");
        toyota.honk();
        jeep.honk();
        porsche.honk();
        // Car hierarchy is closed: only Sedan, SUV, SportsCar

        System.out.println("\n3. NON-SEALED (Truck):");
        Truck ford = new Truck("Ford");
        PickupTruck pickup = new PickupTruck("Chevrolet");
        SemiTruck semi = new SemiTruck("Peterbilt");
        MonsterTruck monster = new MonsterTruck("BigFoot");
        ford.loadCargo();
        pickup.loadCargo();
        semi.loadCargo();
        monster.loadCargo();
        // Truck hierarchy is open - anyone can extend

        System.out.println("\n=== Hierarchy Summary ===");
        System.out.println("""
            Vehicle (sealed)
            ├── Motorcycle (final)
            │   └── [closed - no subclasses]
            │
            ├── Car (sealed)
            │   ├── Sedan (final)
            │   ├── SUV (final)
            │   └── SportsCar (final)
            │
            └── Truck (non-sealed)
                ├── PickupTruck
                │   └── MonsterTruck
                └── SemiTruck
            """);

        System.out.println("=== When to Use Each ===");
        System.out.println("""
            FINAL:
            - Leaf classes in your hierarchy
            - No further specialization needed
            - Most restrictive

            SEALED:
            - Want to control next level too
            - Multi-level controlled hierarchy
            - Must list all permitted subclasses

            NON-SEALED:
            - Open extension point
            - Let users extend your class
            - Escape hatch from sealed hierarchy
            """);
    }
}

final = no more subclasses. sealed = controlled. non-sealed = open again.

permits Lists allowed subclasses: `permits Circle, Rectangle, Triangle`.

Sealed interfaces

Interfaces can be sealed too.

SealedInterfaces.java
// Sealed Interfaces

// Sealed interface - works same as sealed class
sealed interface Payment permits CreditCard, DebitCard, DigitalWallet, CreditCardPayment, PayPalPayment {
    String getPaymentMethod();
    boolean process(double amount);
}

// Permitted implementations must be final, sealed, or non-sealed

final class CreditCard implements Payment {
    private String cardNumber;
    private String expiry;

    CreditCard(String cardNumber, String expiry) {
        this.cardNumber = cardNumber;
        this.expiry = expiry;
    }

    @Override
    public String getPaymentMethod() {
        return "Credit Card ending in " + cardNumber.substring(cardNumber.length() - 4);
    }

    @Override
    public boolean process(double amount) {
        System.out.println("Processing $" + amount + " via credit card");
        return true;
    }
}

final class DebitCard implements Payment {
    private String cardNumber;
    private String pin;

    DebitCard(String cardNumber, String pin) {
        this.cardNumber = cardNumber;
        this.pin = pin;
    }

    @Override
    public String getPaymentMethod() {
        return "Debit Card ending in " + cardNumber.substring(cardNumber.length() - 4);
    }

    @Override
    public boolean process(double amount) {
        System.out.println("Processing $" + amount + " via debit card");
        return true;
    }
}

// Sealed implementation that permits further
sealed class DigitalWallet implements Payment permits PayPal, ApplePay, GooglePay {
    protected String accountId;

    DigitalWallet(String accountId) {
        this.accountId = accountId;
    }

    @Override
    public String getPaymentMethod() {
        return "Digital Wallet: " + accountId;
    }

    @Override
    public boolean process(double amount) {
        System.out.println("Processing $" + amount + " via digital wallet");
        return true;
    }
}

final class PayPal extends DigitalWallet {
    PayPal(String email) {
        super(email);
    }

    @Override
    public String getPaymentMethod() {
        return "PayPal: " + accountId;
    }
}

final class ApplePay extends DigitalWallet {
    ApplePay(String deviceId) {
        super(deviceId);
    }

    @Override
    public String getPaymentMethod() {
        return "Apple Pay: " + accountId;
    }
}

final class GooglePay extends DigitalWallet {
    GooglePay(String email) {
        super(email);
    }

    @Override
    public String getPaymentMethod() {
        return "Google Pay: " + accountId;
    }
}

// Multiple sealed interfaces
sealed interface Refundable permits CreditCardPayment, PayPalPayment {
    void refund(double amount);
}

// Class can implement multiple sealed interfaces
final class CreditCardPayment implements Payment, Refundable {
    private String cardNumber;

    CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public String getPaymentMethod() {
        return "Credit Card: " + cardNumber;
    }

    @Override
    public boolean process(double amount) {
        System.out.println("Processing $" + amount);
        return true;
    }

    @Override
    public void refund(double amount) {
        System.out.println("Refunding $" + amount + " to credit card");
    }
}

final class PayPalPayment implements Payment, Refundable {
    private String email;

    PayPalPayment(String email) {
        this.email = email;
    }

    @Override
    public String getPaymentMethod() {
        return "PayPal: " + email;
    }

    @Override
    public boolean process(double amount) {
        System.out.println("Processing $" + amount + " via PayPal");
        return true;
    }

    @Override
    public void refund(double amount) {
        System.out.println("Refunding $" + amount + " to PayPal");
    }
}

public class SealedInterfaces {
    public static void main(String[] args) {
        System.out.println("=== Sealed Interfaces ===\n");

        Payment credit = new CreditCard("1234567890123456", "12/25");
        Payment debit = new DebitCard("9876543210987654", "1234");
        Payment wallet = new PayPal("user@example.com");

        System.out.println("--- Payment Methods ---");
        System.out.println(credit.getPaymentMethod());
        System.out.println(debit.getPaymentMethod());
        System.out.println(wallet.getPaymentMethod());

        System.out.println("\n--- Processing Payments ---");
        credit.process(99.99);
        debit.process(49.99);
        wallet.process(29.99);

        System.out.println("\n--- Digital Wallet Implementations ---");
        DigitalWallet paypal = new PayPal("john@example.com");
        DigitalWallet apple = new ApplePay("device-12345");
        DigitalWallet google = new GooglePay("jane@gmail.com");

        System.out.println(paypal.getPaymentMethod());
        System.out.println(apple.getPaymentMethod());
        System.out.println(google.getPaymentMethod());

        System.out.println("\n--- Multiple Sealed Interfaces ---");
        Refundable refundable = new CreditCardPayment("1111222233334444");
        refundable.refund(25.00);

        System.out.println("\n=== Hierarchy ===");
        System.out.println("""
            Payment (sealed interface)
            ├── CreditCard (final class)
            ├── DebitCard (final class)
            └── DigitalWallet (sealed class)
                ├── PayPal (final)
                ├── ApplePay (final)
                └── GooglePay (final)

            Refundable (sealed interface)
            ├── CreditCardPayment (final, also implements Payment)
            └── PayPalPayment (final, also implements Payment)
            """);
    }
}

sealed interface works the same way. Implementing classes must be permitted.

Pattern matching with sealed

Exhaustive switch - compiler knows all cases.

PatternMatching.java
// Pattern Matching with Sealed Classes

// Sealed hierarchy for expressions
sealed interface Expr permits Num, Add, Mul, Neg {
    // Evaluate the expression
    int eval();
}

// Permitted implementations

final class Num implements Expr {
    private final int value;

    Num(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    @Override
    public int eval() {
        return value;
    }

    @Override
    public String toString() {
        return String.valueOf(value);
    }
}

final class Add implements Expr {
    private final Expr left;
    private final Expr right;

    Add(Expr left, Expr right) {
        this.left = left;
        this.right = right;
    }

    public Expr getLeft() { return left; }
    public Expr getRight() { return right; }

    @Override
    public int eval() {
        return left.eval() + right.eval();
    }

    @Override
    public String toString() {
        return "(" + left + " + " + right + ")";
    }
}

final class Mul implements Expr {
    private final Expr left;
    private final Expr right;

    Mul(Expr left, Expr right) {
        this.left = left;
        this.right = right;
    }

    public Expr getLeft() { return left; }
    public Expr getRight() { return right; }

    @Override
    public int eval() {
        return left.eval() * right.eval();
    }

    @Override
    public String toString() {
        return "(" + left + " * " + right + ")";
    }
}

final class Neg implements Expr {
    private final Expr expr;

    Neg(Expr expr) {
        this.expr = expr;
    }

    public Expr getExpr() { return expr; }

    @Override
    public int eval() {
        return -expr.eval();
    }

    @Override
    public String toString() {
        return "(-" + expr + ")";
    }
}

public class PatternMatching {
    public static void main(String[] args) {
        System.out.println("=== Pattern Matching with Sealed Classes ===\n");

        // Build expressions
        Expr simple = new Num(42);
        Expr addition = new Add(new Num(10), new Num(20));
        Expr complex = new Mul(
            new Add(new Num(2), new Num(3)),
            new Neg(new Num(4))
        );  // (2 + 3) * (-4) = -20

        System.out.println("--- Evaluation ---");
        System.out.println(simple + " = " + simple.eval());
        System.out.println(addition + " = " + addition.eval());
        System.out.println(complex + " = " + complex.eval());

        // Pattern matching in switch - EXHAUSTIVE!
        System.out.println("\n--- Pattern Matching ---");
        describeExpr(simple);
        describeExpr(addition);
        describeExpr(complex);

        System.out.println("\n--- Transformation ---");
        Expr doubled = transform(addition);
        System.out.println("Original: " + addition + " = " + addition.eval());
        System.out.println("Doubled:  " + doubled + " = " + doubled.eval());

        System.out.println("\n=== Why Exhaustive Matters ===");
        System.out.println("""
            With sealed classes, the compiler KNOWS all subclasses.
            Switch can check if all cases are covered!

            If we add a new Expr type (e.g., Div):
            - Compiler shows error in every switch
            - Forces us to handle new case
            - No runtime surprises!
            """);
    }

    // Pattern matching with exhaustive switch
    static void describeExpr(Expr expr) {
        String description = switch (expr) {
            case Num n -> "Number: " + n.getValue();
            case Add a -> "Addition of " + a.getLeft() + " and " + a.getRight();
            case Mul m -> "Multiplication of " + m.getLeft() + " and " + m.getRight();
            case Neg n -> "Negation of " + n.getExpr();
            // No default needed! Compiler knows all cases covered
        };
        System.out.println(description);
    }

    // Transform expression using pattern matching
    static Expr transform(Expr expr) {
        return switch (expr) {
            case Num n -> new Num(n.getValue() * 2);  // Double numbers
            case Add a -> new Add(transform(a.getLeft()), transform(a.getRight()));
            case Mul m -> new Mul(transform(m.getLeft()), transform(m.getRight()));
            case Neg n -> new Neg(transform(n.getExpr()));
        };
    }

    // If we add guards
    static String evaluate(Expr expr) {
        return switch (expr) {
            case Num n when n.getValue() == 0 -> "Zero";
            case Num n when n.getValue() > 0 -> "Positive: " + n.getValue();
            case Num n -> "Negative: " + n.getValue();  // Must come last for Num
            case Add a -> "Sum: " + a.eval();
            case Mul m -> "Product: " + m.eval();
            case Neg n -> "Negated: " + n.eval();
        };
    }
}

Switch on sealed type is exhaustive - no default needed.

Records with sealed

Combine records and sealed for algebraic data types.

RecordsInSealed.java
// Records with Sealed Classes

// Sealed interface with record implementations
sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
    double perimeter();
}

// Records are implicitly final - perfect for sealed!
record Circle(double radius) implements Shape {
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

record Rectangle(double width, double height) implements Shape {
    @Override
    public double area() {
        return width * height;
    }

    @Override
    public double perimeter() {
        return 2 * (width + height);
    }
}

record Triangle(double a, double b, double c) implements Shape {
    // Compact constructor for validation
    public Triangle {
        if (a + b <= c || b + c <= a || a + c <= b) {
            throw new IllegalArgumentException("Invalid triangle sides");
        }
    }

    @Override
    public double area() {
        double s = (a + b + c) / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    @Override
    public double perimeter() {
        return a + b + c;
    }
}

// Pattern matching with record deconstruction
class ShapeProcessor {
    // Exhaustive switch with record patterns
    static String describe(Shape shape) {
        return switch (shape) {
            case Circle(var r) ->
                String.format("Circle with radius %.2f", r);
            case Rectangle(var w, var h) when w == h ->
                String.format("Square with side %.2f", w);
            case Rectangle(var w, var h) ->
                String.format("Rectangle %.2f x %.2f", w, h);
            case Triangle(var a, var b, var c) when a == b && b == c ->
                String.format("Equilateral triangle with side %.2f", a);
            case Triangle(var a, var b, var c) ->
                String.format("Triangle with sides %.2f, %.2f, %.2f", a, b, c);
        };
    }

    // Calculate total area
    static double totalArea(Shape... shapes) {
        double total = 0;
        for (Shape shape : shapes) {
            total += shape.area();
        }
        return total;
    }

    // Scale shape
    static Shape scale(Shape shape, double factor) {
        return switch (shape) {
            case Circle(var r) -> new Circle(r * factor);
            case Rectangle(var w, var h) -> new Rectangle(w * factor, h * factor);
            case Triangle(var a, var b, var c) -> new Triangle(a * factor, b * factor, c * factor);
        };
    }
}

// Result type using sealed + records
sealed interface Result<T> permits Success, Failure {
    boolean isSuccess();
}

record Success<T>(T value) implements Result<T> {
    @Override
    public boolean isSuccess() {
        return true;
    }
}

record Failure<T>(String error) implements Result<T> {
    @Override
    public boolean isSuccess() {
        return false;
    }
}

public class RecordsInSealed {
    public static void main(String[] args) {
        System.out.println("=== Records with Sealed Classes ===\n");

        // Create shapes using records
        Circle circle = new Circle(5);
        Rectangle rect = new Rectangle(4, 6);
        Rectangle square = new Rectangle(5, 5);
        Triangle equilateral = new Triangle(3, 3, 3);
        Triangle scalene = new Triangle(3, 4, 5);

        Shape[] shapes = {circle, rect, square, equilateral, scalene};

        System.out.println("--- Shape Descriptions ---");
        for (Shape shape : shapes) {
            System.out.println(ShapeProcessor.describe(shape));
            System.out.printf("  Area: %.2f, Perimeter: %.2f%n",
                shape.area(), shape.perimeter());
        }

        System.out.println("\n--- Total Area ---");
        System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));

        // Scaling
        System.out.println("\n--- Scaling ---");
        double scaleFactor = ;
        Shape scaledCircle = ShapeProcessor.scale(circle, scaleFactor);
        System.out.println("Original: " + circle);
        System.out.println("Scaled " + scaleFactor + "x: " + scaledCircle);

        // Result type example
        System.out.println("\n--- Result Type ---");
        Result<Integer> success = new Success<>(42);
        Result<Integer> failure = new Failure<>("Division by zero");

        processResult(success);
        processResult(failure);

        System.out.println("\n=== Benefits ===");
        System.out.println("""
            Records + Sealed:
            1. Records are implicitly final (perfect for sealed permits)
            2. Record patterns enable deconstruction in switch
            3. Compact syntax for value objects
            4. Automatic equals/hashCode/toString
            5. Guards can add extra conditions

            Common patterns:
            - Shape hierarchies
            - Expression trees (AST)
            - Result/Either types
            - Event types
            - Command patterns
            """);
    }

    // Process result with pattern matching
    static void processResult(Result<Integer> result) {
        switch (result) {
            case Success<Integer>(var value) ->
                System.out.println("Success: " + value);
            case Failure<Integer>(var error) ->
                System.out.println("Failure: " + error);
        }
    }
}
// Records with Sealed Classes

// Sealed interface with record implementations
sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
    double perimeter();
}

// Records are implicitly final - perfect for sealed!
record Circle(double radius) implements Shape {
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

record Rectangle(double width, double height) implements Shape {
    @Override
    public double area() {
        return width * height;
    }

    @Override
    public double perimeter() {
        return 2 * (width + height);
    }
}

record Triangle(double a, double b, double c) implements Shape {
    // Compact constructor for validation
    public Triangle {
        if (a + b <= c || b + c <= a || a + c <= b) {
            throw new IllegalArgumentException("Invalid triangle sides");
        }
    }

    @Override
    public double area() {
        double s = (a + b + c) / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    @Override
    public double perimeter() {
        return a + b + c;
    }
}

// Pattern matching with record deconstruction
class ShapeProcessor {
    // Exhaustive switch with record patterns
    static String describe(Shape shape) {
        return switch (shape) {
            case Circle(var r) ->
                String.format("Circle with radius %.2f", r);
            case Rectangle(var w, var h) when w == h ->
                String.format("Square with side %.2f", w);
            case Rectangle(var w, var h) ->
                String.format("Rectangle %.2f x %.2f", w, h);
            case Triangle(var a, var b, var c) when a == b && b == c ->
                String.format("Equilateral triangle with side %.2f", a);
            case Triangle(var a, var b, var c) ->
                String.format("Triangle with sides %.2f, %.2f, %.2f", a, b, c);
        };
    }

    // Calculate total area
    static double totalArea(Shape... shapes) {
        double total = 0;
        for (Shape shape : shapes) {
            total += shape.area();
        }
        return total;
    }

    // Scale shape
    static Shape scale(Shape shape, double factor) {
        return switch (shape) {
            case Circle(var r) -> new Circle(r * factor);
            case Rectangle(var w, var h) -> new Rectangle(w * factor, h * factor);
            case Triangle(var a, var b, var c) -> new Triangle(a * factor, b * factor, c * factor);
        };
    }
}

// Result type using sealed + records
sealed interface Result<T> permits Success, Failure {
    boolean isSuccess();
}

record Success<T>(T value) implements Result<T> {
    @Override
    public boolean isSuccess() {
        return true;
    }
}

record Failure<T>(String error) implements Result<T> {
    @Override
    public boolean isSuccess() {
        return false;
    }
}

public class RecordsInSealed {
    public static void main(String[] args) {
        System.out.println("=== Records with Sealed Classes ===\n");

        // Create shapes using records
        Circle circle = new Circle(5);
        Rectangle rect = new Rectangle(4, 6);
        Rectangle square = new Rectangle(5, 5);
        Triangle equilateral = new Triangle(3, 3, 3);
        Triangle scalene = new Triangle(3, 4, 5);

        Shape[] shapes = {circle, rect, square, equilateral, scalene};

        System.out.println("--- Shape Descriptions ---");
        for (Shape shape : shapes) {
            System.out.println(ShapeProcessor.describe(shape));
            System.out.printf("  Area: %.2f, Perimeter: %.2f%n",
                shape.area(), shape.perimeter());
        }

        System.out.println("\n--- Total Area ---");
        System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));

        // Scaling
        System.out.println("\n--- Scaling ---");
        double scaleFactor = ;
        Shape scaledCircle = ShapeProcessor.scale(circle, scaleFactor);
        System.out.println("Original: " + circle);
        System.out.println("Scaled " + scaleFactor + "x: " + scaledCircle);

        // Result type example
        System.out.println("\n--- Result Type ---");
        Result<Integer> success = new Success<>(42);
        Result<Integer> failure = new Failure<>("Division by zero");

        processResult(success);
        processResult(failure);

        System.out.println("\n=== Benefits ===");
        System.out.println("""
            Records + Sealed:
            1. Records are implicitly final (perfect for sealed permits)
            2. Record patterns enable deconstruction in switch
            3. Compact syntax for value objects
            4. Automatic equals/hashCode/toString
            5. Guards can add extra conditions

            Common patterns:
            - Shape hierarchies
            - Expression trees (AST)
            - Result/Either types
            - Event types
            - Command patterns
            """);
    }

    // Process result with pattern matching
    static void processResult(Result<Integer> result) {
        switch (result) {
            case Success<Integer>(var value) ->
                System.out.println("Success: " + value);
            case Failure<Integer>(var error) ->
                System.out.println("Failure: " + error);
        }
    }
}
// Records with Sealed Classes

// Sealed interface with record implementations
sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
    double perimeter();
}

// Records are implicitly final - perfect for sealed!
record Circle(double radius) implements Shape {
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

record Rectangle(double width, double height) implements Shape {
    @Override
    public double area() {
        return width * height;
    }

    @Override
    public double perimeter() {
        return 2 * (width + height);
    }
}

record Triangle(double a, double b, double c) implements Shape {
    // Compact constructor for validation
    public Triangle {
        if (a + b <= c || b + c <= a || a + c <= b) {
            throw new IllegalArgumentException("Invalid triangle sides");
        }
    }

    @Override
    public double area() {
        double s = (a + b + c) / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    @Override
    public double perimeter() {
        return a + b + c;
    }
}

// Pattern matching with record deconstruction
class ShapeProcessor {
    // Exhaustive switch with record patterns
    static String describe(Shape shape) {
        return switch (shape) {
            case Circle(var r) ->
                String.format("Circle with radius %.2f", r);
            case Rectangle(var w, var h) when w == h ->
                String.format("Square with side %.2f", w);
            case Rectangle(var w, var h) ->
                String.format("Rectangle %.2f x %.2f", w, h);
            case Triangle(var a, var b, var c) when a == b && b == c ->
                String.format("Equilateral triangle with side %.2f", a);
            case Triangle(var a, var b, var c) ->
                String.format("Triangle with sides %.2f, %.2f, %.2f", a, b, c);
        };
    }

    // Calculate total area
    static double totalArea(Shape... shapes) {
        double total = 0;
        for (Shape shape : shapes) {
            total += shape.area();
        }
        return total;
    }

    // Scale shape
    static Shape scale(Shape shape, double factor) {
        return switch (shape) {
            case Circle(var r) -> new Circle(r * factor);
            case Rectangle(var w, var h) -> new Rectangle(w * factor, h * factor);
            case Triangle(var a, var b, var c) -> new Triangle(a * factor, b * factor, c * factor);
        };
    }
}

// Result type using sealed + records
sealed interface Result<T> permits Success, Failure {
    boolean isSuccess();
}

record Success<T>(T value) implements Result<T> {
    @Override
    public boolean isSuccess() {
        return true;
    }
}

record Failure<T>(String error) implements Result<T> {
    @Override
    public boolean isSuccess() {
        return false;
    }
}

public class RecordsInSealed {
    public static void main(String[] args) {
        System.out.println("=== Records with Sealed Classes ===\n");

        // Create shapes using records
        Circle circle = new Circle(5);
        Rectangle rect = new Rectangle(4, 6);
        Rectangle square = new Rectangle(5, 5);
        Triangle equilateral = new Triangle(3, 3, 3);
        Triangle scalene = new Triangle(3, 4, 5);

        Shape[] shapes = {circle, rect, square, equilateral, scalene};

        System.out.println("--- Shape Descriptions ---");
        for (Shape shape : shapes) {
            System.out.println(ShapeProcessor.describe(shape));
            System.out.printf("  Area: %.2f, Perimeter: %.2f%n",
                shape.area(), shape.perimeter());
        }

        System.out.println("\n--- Total Area ---");
        System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));

        // Scaling
        System.out.println("\n--- Scaling ---");
        double scaleFactor = ;
        Shape scaledCircle = ShapeProcessor.scale(circle, scaleFactor);
        System.out.println("Original: " + circle);
        System.out.println("Scaled " + scaleFactor + "x: " + scaledCircle);

        // Result type example
        System.out.println("\n--- Result Type ---");
        Result<Integer> success = new Success<>(42);
        Result<Integer> failure = new Failure<>("Division by zero");

        processResult(success);
        processResult(failure);

        System.out.println("\n=== Benefits ===");
        System.out.println("""
            Records + Sealed:
            1. Records are implicitly final (perfect for sealed permits)
            2. Record patterns enable deconstruction in switch
            3. Compact syntax for value objects
            4. Automatic equals/hashCode/toString
            5. Guards can add extra conditions

            Common patterns:
            - Shape hierarchies
            - Expression trees (AST)
            - Result/Either types
            - Event types
            - Command patterns
            """);
    }

    // Process result with pattern matching
    static void processResult(Result<Integer> result) {
        switch (result) {
            case Success<Integer>(var value) ->
                System.out.println("Success: " + value);
            case Failure<Integer>(var error) ->
                System.out.println("Failure: " + error);
        }
    }
}

Records as permitted subclasses give concise data carriers with controlled hierarchy.

Exercise: Practical.java

Model a state machine with sealed classes