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
Replay: real traced execution (multi-file project)
// 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'
            """);
    }
}
  1. public static void main(String[] args)

    54public class SealedBasics {55    public static void main(String[] args) {56        System.out.println("=== Sealed Classes Basics ===\n");57        58        // Create permitted subclasses //?createobjects59        Dog dog = new Dog("Buddy");60        Cat cat = new Cat("Whiskers");
    output=== Sealed Classes Basics ===
  2. this.name ← Buddy

    pass 1 of 3
    7Animal(String nameBuddy) {8    this.name→ Buddy = nameBuddy;9}
    All 3 passes — pass 1 is the card above
    passnamethis.name
    1BuddyBuddy
    2WhiskersWhiskers
    3TweetyTweety
  3. Dog(String name)

    21final class Dog extends Animal { //?finalsubclass22    Dog(String nameBuddy) {23        super(name);
  4. dog ← ⟨Dog A⟩

    58// Create permitted subclasses //?createobjects59Dog dog→ ⟨Dog A⟩ = new Dog("Buddy");60Cat cat = new Cat("Whiskers");61Bird bird = new Bird("Tweety");
  5. Cat(String name)

    31final class Cat extends Animal { //?anothersubclass32    Cat(String nameWhiskers) {33        super(name);
  6. cat ← ⟨Cat B⟩

    59Dog dog = new Dog("Buddy");60Cat cat→ ⟨Cat B⟩ = new Cat("Whiskers");61Bird bird = new Bird("Tweety");
  7. Bird(String name)

    41final class Bird extends Animal {42    Bird(String nameTweety) {43        super(name);
  8. bird ← ⟨Bird C⟩

    60Cat cat = new Cat("Whiskers");61Bird bird→ ⟨Bird C⟩ = new Bird("Tweety");6263dog.describe();64dog.bark();
  9. public void describe()

    pass 1 of 6
    15public void describe() {16    System.out.println("I am " + nameBuddy);17}
    outputI am Buddy
    All 6 passes — pass 1 is the card above
    passname
    1Buddy
    2Whiskers
    3Tweety
    4Buddy
    5Whiskers
    6Tweety
  10. dog.describe();

    63dog.describe();64dog.bark();
  11. public String getName()

    pass 1 of 3
    11public String getName() {12    return nameBuddy;13}
    All 3 passes — pass 1 is the card above
    passname
    1Buddy
    2Whiskers
    3Tweety
  12. System.out.println(getName() + " says: Woof!");

    26public void bark() {27    System.out.println(getName() + " says: Woof!");28}
    outputBuddy says: Woof!
  13. dog.bark();

    63dog.describe();64dog.bark();6566System.out.println();67cat.describe();68cat.meow();
  14. cat.describe();

    66System.out.println();67cat.describe();68cat.meow();
  15. System.out.println(getName() + " says: Meow!");

    36public void meow() {37    System.out.println(getName() + " says: Meow!");38}
    outputWhiskers says: Meow!
  16. cat.meow();

    67cat.describe();68cat.meow();6970System.out.println();71bird.describe();72bird.chirp();
  17. bird.describe();

    70System.out.println();71bird.describe();72bird.chirp();
  18. System.out.println(getName() + " says: Chirp!");

    46public void chirp() {47    System.out.println(getName() + " says: Chirp!");48}
    outputTweety says: Chirp!
  19. Animal[] animals = {dog, cat, bird}; //?polymorphism

    71bird.describe();72bird.chirp();7374System.out.println("\n=== Polymorphism Works ===");7576Animal[] animals = {dog, cat, bird}; //?polymorphism77for (Animal animal : animals) {
    output
    === Polymorphism Works ===
  20. for (Animal animal : animals)

    pass 1 of 3
    76Animal[] animals = {dog, cat, bird}; //?polymorphism77for (Animal animal⟨Dog A⟩ : animals) {78    animal.describe();79}
    All 3 passes — pass 1 is the card above
    passanimal
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Bird C⟩
  21. animal.describe();

    77for (Animal animal : animals) {78    animal.describe();79}
  22. animal.describe();

    77for (Animal animal : animals) {78    animal.describe();79}
  23. animal.describe();

    77for (Animal animal : animals) {78    animal.describe();79}
  24. System.out.println(" === Why Sealed Classes? ===");

    81    System.out.println("\n=== Why Sealed Classes? ===");82    System.out.println("""83        1. CONTROL: Know exactly which classes extend yours84        2. SAFETY: Prevent unexpected subclasses85        3. EXHAUSTIVE: Switch can cover all cases86        4. DOCUMENTATION: Permits list shows intent87        88        Without sealed:89        - Anyone can extend your class90        - Switch might miss cases91        - Library users could break assumptions92        93        With sealed:94        - Only permitted classes can extend95        - Compiler knows all subclasses96        - Pattern matching is exhaustive97        """);98    99    System.out.println("=== Sealed Class Rules ===");100    System.out.println("""101        1. Use 'sealed' modifier on class102        2. Use 'permits' to list allowed subclasses103        3. Each permitted class must:104           - Be in same module (or package if unnamed)105           - Directly extend the sealed class106           - Be 'final', 'sealed', or 'non-sealed'107        """);108}
    output
    === Why Sealed Classes? ===
    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
    === Sealed Class Rules ===
    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
Replay: real traced execution (multi-file project)
// 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
            """);
    }
}
  1. public static void main(String[] args)

    89public class PermitsOptions {90    public static void main(String[] args) {91        System.out.println("=== Three Options for Permitted Classes ===\n");92        93        System.out.println("1. FINAL (Motorcycle):");94        Motorcycle harley = new Motorcycle("Harley");95        harley.wheelie();
    output=== Three Options for Permitted Classes ===
    1. FINAL (Motorcycle):
  2. this.brand ← Harley

    pass 1 of 8
    7Vehicle(String brandHarley) {8    this.brand→ Harley = brandHarley;9}
    All 8 passes — pass 1 is the card above
    passbrandthis.brand
    1HarleyHarley
    2ToyotaToyota
    3JeepJeep
    4PorschePorsche
    5FordFord
    6ChevroletChevrolet
    7PeterbiltPeterbilt
    8BigFootBigFoot
  3. Motorcycle(String brand)

    17final class Motorcycle extends Vehicle { //?motorcyclefinal18    Motorcycle(String brandHarley) {19        super(brand);
  4. harley ← ⟨Motorcycle A⟩

    93System.out.println("1. FINAL (Motorcycle):");94Motorcycle harley→ ⟨Motorcycle A⟩ = new Motorcycle("Harley");95harley.wheelie();96// Cannot create subclass of Motorcycle
  5. public String getBrand()

    pass 1 of 8
    11public String getBrand() {12    return brandHarley;13}
    All 8 passes — pass 1 is the card above
    passbrand
    1Harley
    2Toyota
    3Jeep
    4Porsche
    5Ford
    6Chevrolet
    7Peterbilt
    8BigFoot
  6. System.out.println(getBrand() + " motorcycle doing a wheelie!");

    22public void wheelie() {23    System.out.println(getBrand() + " motorcycle doing a wheelie!");24}
    outputHarley motorcycle doing a wheelie!
  7. harley.wheelie();

    94Motorcycle harley = new Motorcycle("Harley");95harley.wheelie();96// Cannot create subclass of Motorcycle9798System.out.println("\n2. SEALED (Car):");99Sedan toyota = new Sedan("Toyota");100SUV jeep = new SUV("Jeep");
    output
    2. SEALED (Car):
  8. Car(String brand)

    pass 1 of 3
    29sealed class Car extends Vehicle permits Sedan, SUV, SportsCar { //?carpermits30    Car(String brandToyota) {31        super(brand);
    All 3 passes — pass 1 is the card above
    passbrand
    1Toyota
    2Jeep
    3Porsche
  9. Sedan(String brand)

    40final class Sedan extends Car { //?sedanfinal41    Sedan(String brandToyota) {42        super(brand);
  10. toyota ← ⟨Sedan B⟩

    98System.out.println("\n2. SEALED (Car):");99Sedan toyota→ ⟨Sedan B⟩ = new Sedan("Toyota");100SUV jeep = new SUV("Jeep");101SportsCar porsche = new SportsCar("Porsche");
  11. SUV(String brand)

    46final class SUV extends Car {47    SUV(String brandJeep) {48        super(brand);
  12. jeep ← ⟨SUV C⟩

    99Sedan toyota = new Sedan("Toyota");100SUV jeep→ ⟨SUV C⟩ = new SUV("Jeep");101SportsCar porsche = new SportsCar("Porsche");102toyota.honk();
  13. SportsCar(String brand)

    52final class SportsCar extends Car {53    SportsCar(String brandPorsche) {54        super(brand);
  14. porsche ← ⟨SportsCar D⟩

    100SUV jeep = new SUV("Jeep");101SportsCar porsche→ ⟨SportsCar D⟩ = new SportsCar("Porsche");102toyota.honk();103jeep.honk();
  15. System.out.println(getBrand() + " car: Beep beep!");

    34public void honk() {35    System.out.println(getBrand() + " car: Beep beep!");36}
    outputToyota car: Beep beep!
  16. toyota.honk();

    101SportsCar porsche = new SportsCar("Porsche");102toyota.honk();103jeep.honk();104porsche.honk();
  17. System.out.println(getBrand() + " car: Beep beep!");

    34public void honk() {35    System.out.println(getBrand() + " car: Beep beep!");36}
    outputJeep car: Beep beep!
  18. jeep.honk();

    102toyota.honk();103jeep.honk();104porsche.honk();105// Car hierarchy is closed: only Sedan, SUV, SportsCar
  19. System.out.println(getBrand() + " car: Beep beep!");

    34public void honk() {35    System.out.println(getBrand() + " car: Beep beep!");36}
    outputPorsche car: Beep beep!
  20. porsche.honk();

    103jeep.honk();104porsche.honk();105// Car hierarchy is closed: only Sedan, SUV, SportsCar106107System.out.println("\n3. NON-SEALED (Truck):");108Truck ford = new Truck("Ford");109PickupTruck pickup = new PickupTruck("Chevrolet");
    output
    3. NON-SEALED (Truck):
  21. Truck(String brand)

    pass 1 of 4
    59non-sealed class Truck extends Vehicle { //?trucknonseal60    Truck(String brandFord) {61        super(brand);
    All 4 passes — pass 1 is the card above
    passbrand
    1Ford
    2Chevrolet
    3Peterbilt
    4BigFoot
  22. ford ← ⟨Truck E⟩

    107System.out.println("\n3. NON-SEALED (Truck):");108Truck ford→ ⟨Truck E⟩ = new Truck("Ford");109PickupTruck pickup = new PickupTruck("Chevrolet");110SemiTruck semi = new SemiTruck("Peterbilt");
  23. PickupTruck(String brand)

    pass 1 of 2
    70class PickupTruck extends Truck { //?pickup71    PickupTruck(String brandChevrolet) {72        super(brand);
  24. pickup ← ⟨PickupTruck F⟩

    108Truck ford = new Truck("Ford");109PickupTruck pickup→ ⟨PickupTruck F⟩ = new PickupTruck("Chevrolet");110SemiTruck semi = new SemiTruck("Peterbilt");111MonsterTruck monster = new MonsterTruck("BigFoot");
  25. SemiTruck(String brand)

    76class SemiTruck extends Truck { //?semi77    SemiTruck(String brandPeterbilt) {78        super(brand);
  26. semi ← ⟨SemiTruck G⟩

    109PickupTruck pickup = new PickupTruck("Chevrolet");110SemiTruck semi→ ⟨SemiTruck G⟩ = new SemiTruck("Peterbilt");111MonsterTruck monster = new MonsterTruck("BigFoot");112ford.loadCargo();
  27. PickupTruck(String brand)

    pass 2 of 2
    70class PickupTruck extends Truck { //?pickup71    PickupTruck(String brandBigFoot) {72        super(brand);
  28. MonsterTruck(String brand)

    83class MonsterTruck extends PickupTruck {84    MonsterTruck(String brandBigFoot) {85        super(brand);
  29. monster ← ⟨MonsterTruck H⟩

    110SemiTruck semi = new SemiTruck("Peterbilt");111MonsterTruck monster→ ⟨MonsterTruck H⟩ = new MonsterTruck("BigFoot");112ford.loadCargo();113pickup.loadCargo();
  30. System.out.println(getBrand() + " truck loading cargo");

    64public void loadCargo() {65    System.out.println(getBrand() + " truck loading cargo");66}
    outputFord truck loading cargo
  31. ford.loadCargo();

    111MonsterTruck monster = new MonsterTruck("BigFoot");112ford.loadCargo();113pickup.loadCargo();114semi.loadCargo();
  32. System.out.println(getBrand() + " truck loading cargo");

    64public void loadCargo() {65    System.out.println(getBrand() + " truck loading cargo");66}
    outputChevrolet truck loading cargo
  33. pickup.loadCargo();

    112ford.loadCargo();113pickup.loadCargo();114semi.loadCargo();115monster.loadCargo();
  34. System.out.println(getBrand() + " truck loading cargo");

    64public void loadCargo() {65    System.out.println(getBrand() + " truck loading cargo");66}
    outputPeterbilt truck loading cargo
  35. semi.loadCargo();

    113pickup.loadCargo();114semi.loadCargo();115monster.loadCargo();116// Truck hierarchy is open - anyone can extend
  36. System.out.println(getBrand() + " truck loading cargo");

    64public void loadCargo() {65    System.out.println(getBrand() + " truck loading cargo");66}
    outputBigFoot truck loading cargo
  37. monster.loadCargo();

    114    semi.loadCargo();115    monster.loadCargo();116    // Truck hierarchy is open - anyone can extend117    118    System.out.println("\n=== Hierarchy Summary ===");119    System.out.println("""120        Vehicle (sealed)121        ├── Motorcycle (final)122        │   └── [closed - no subclasses]123124        ├── Car (sealed)125        │   ├── Sedan (final)126        │   ├── SUV (final)127        │   └── SportsCar (final)128129        └── Truck (non-sealed)130            ├── PickupTruck131            │   └── MonsterTruck132            └── SemiTruck133        """);134    135    System.out.println("=== When to Use Each ===");136    System.out.println("""137        FINAL:138        - Leaf classes in your hierarchy139        - No further specialization needed140        - Most restrictive141        142        SEALED:143        - Want to control next level too144        - Multi-level controlled hierarchy145        - Must list all permitted subclasses146        147        NON-SEALED:148        - Open extension point149        - Let users extend your class150        - Escape hatch from sealed hierarchy151        """);152}
    output
    === Hierarchy Summary ===
    Vehicle (sealed)
    ├── Motorcycle (final)
    │   └── [closed - no subclasses]
    │
    ├── Car (sealed)
    │   ├── Sedan (final)
    │   ├── SUV (final)
    │   └── SportsCar (final)
    │
    └── Truck (non-sealed)
        ├── PickupTruck
        │   └── MonsterTruck
        └── SemiTruck
    === When to Use Each ===
    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
Replay: real traced execution (multi-file project)
// 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)
            """);
    }
}
  1. public static void main(String[] args)

    160public class SealedInterfaces {161    public static void main(String[] args) {162        System.out.println("=== Sealed Interfaces ===\n");163        164        Payment credit = new CreditCard("1234567890123456", "12/25");165        Payment debit = new DebitCard("9876543210987654", "1234");
    output=== Sealed Interfaces ===
  2. this.cardNumber ← 1234567890123456, this.expiry ← 12/25

    15CreditCard(String cardNumber1234567890123456, String expiry12/25) {16    this.cardNumber→ 1234567890123456 = cardNumber1234567890123456;17    this.expiry→ 12/25 = expiry12/25;18}
  3. credit ← ⟨CreditCard A⟩

    164Payment credit→ ⟨CreditCard A⟩ = new CreditCard("1234567890123456", "12/25");165Payment debit = new DebitCard("9876543210987654", "1234");166Payment wallet = new PayPal("user@example.com");
  4. this.cardNumber ← 9876543210987654, this.pin ← 1234

    36DebitCard(String cardNumber9876543210987654, String pin1234) {37    this.cardNumber→ 9876543210987654 = cardNumber9876543210987654;38    this.pin→ 1234 = pin1234;39}
  5. debit ← ⟨DebitCard B⟩

    164Payment credit = new CreditCard("1234567890123456", "12/25");165Payment debit→ ⟨DebitCard B⟩ = new DebitCard("9876543210987654", "1234");166Payment wallet = new PayPal("user@example.com");
  6. this.accountId ← user@example.com

    pass 1 of 4
    57DigitalWallet(String accountIduser@example.com) {58    this.accountId→ user@example.com = accountIduser@example.com;59}
    All 4 passes — pass 1 is the card above
    passaccountIdemaildeviceIdthis.accountId
    1user@example.comuser@example.comuser@example.com
    2john@example.comjohn@example.comjohn@example.com
    3device-12345device-12345device-12345
    4jane@gmail.comjane@gmail.comjane@gmail.com
  7. PayPal(String email)

    pass 1 of 2
    73final class PayPal extends DigitalWallet { //?paypal74    PayPal(String emailuser@example.com) {75        super(email);
  8. wallet ← ⟨PayPal C⟩

    165Payment debit = new DebitCard("9876543210987654", "1234");166Payment wallet→ ⟨PayPal C⟩ = new PayPal("user@example.com");167168System.out.println("--- Payment Methods ---");169System.out.println(credit.getPaymentMethod());170System.out.println(debit.getPaymentMethod());
    output--- Payment Methods ---
  9. System.out.println(credit.getPaymentMethod());

    168System.out.println("--- Payment Methods ---");169System.out.println(credit.getPaymentMethod());170System.out.println(debit.getPaymentMethod());171System.out.println(wallet.getPaymentMethod());
    outputCredit Card ending in 3456
  10. System.out.println(debit.getPaymentMethod());

    169System.out.println(credit.getPaymentMethod());170System.out.println(debit.getPaymentMethod());171System.out.println(wallet.getPaymentMethod());
    outputDebit Card ending in 7654
  11. @Override public String getPaymentMethod()

    pass 1 of 2
    78@Override79public String getPaymentMethod() {80    return "PayPal: " + accountIduser@example.com;81}
  12. System.out.println(wallet.getPaymentMethod());

    170System.out.println(debit.getPaymentMethod());171System.out.println(wallet.getPaymentMethod());172173System.out.println("\n--- Processing Payments ---");174credit.process(99.99);175debit.process(49.99);
    outputPayPal: user@example.com
    
    --- Processing Payments ---
  13. @Override public boolean process(double amount)

    25@Override26public boolean process(double amount99.99) {27    System.out.println("Processing $" + amount99.99 + " via credit card");28    return true;29}
    outputProcessing $99.99 via credit card
  14. credit.process(99.99);

    173System.out.println("\n--- Processing Payments ---");174credit.process(99.99);175debit.process(49.99);176wallet.process(29.99);
  15. @Override public boolean process(double amount)

    46@Override47public boolean process(double amount49.99) {48    System.out.println("Processing $" + amount49.99 + " via debit card");49    return true;50}
    outputProcessing $49.99 via debit card
  16. debit.process(49.99);

    174credit.process(99.99);175debit.process(49.99);176wallet.process(29.99);
  17. @Override public boolean process(double amount)

    66@Override67public boolean process(double amount29.99) {68    System.out.println("Processing $" + amount29.99 + " via digital wallet");69    return true;70}
    outputProcessing $29.99 via digital wallet
  18. wallet.process(29.99);

    175debit.process(49.99);176wallet.process(29.99);177178System.out.println("\n--- Digital Wallet Implementations ---");179DigitalWallet paypal = new PayPal("john@example.com");180DigitalWallet apple = new ApplePay("device-12345");
    output
    --- Digital Wallet Implementations ---
  19. PayPal(String email)

    pass 2 of 2
    73final class PayPal extends DigitalWallet { //?paypal74    PayPal(String emailjohn@example.com) {75        super(email);
  20. paypal ← ⟨PayPal D⟩

    178System.out.println("\n--- Digital Wallet Implementations ---");179DigitalWallet paypal→ ⟨PayPal D⟩ = new PayPal("john@example.com");180DigitalWallet apple = new ApplePay("device-12345");181DigitalWallet google = new GooglePay("jane@gmail.com");
  21. ApplePay(String deviceId)

    84final class ApplePay extends DigitalWallet { //?applepay85    ApplePay(String deviceIddevice-12345) {86        super(deviceId);
  22. apple ← ⟨ApplePay E⟩

    179DigitalWallet paypal = new PayPal("john@example.com");180DigitalWallet apple→ ⟨ApplePay E⟩ = new ApplePay("device-12345");181DigitalWallet google = new GooglePay("jane@gmail.com");
  23. GooglePay(String email)

    95final class GooglePay extends DigitalWallet {96    GooglePay(String emailjane@gmail.com) {97        super(email);
  24. google ← ⟨GooglePay F⟩

    180DigitalWallet apple = new ApplePay("device-12345");181DigitalWallet google→ ⟨GooglePay F⟩ = new GooglePay("jane@gmail.com");182183System.out.println(paypal.getPaymentMethod());184System.out.println(apple.getPaymentMethod());
  25. @Override public String getPaymentMethod()

    pass 2 of 2
    78@Override79public String getPaymentMethod() {80    return "PayPal: " + accountIdjohn@example.com;81}
  26. System.out.println(paypal.getPaymentMethod());

    183System.out.println(paypal.getPaymentMethod());184System.out.println(apple.getPaymentMethod());185System.out.println(google.getPaymentMethod());
    outputPayPal: john@example.com
  27. @Override public String getPaymentMethod()

    89@Override90public String getPaymentMethod() {91    return "Apple Pay: " + accountIddevice-12345;92}
  28. System.out.println(apple.getPaymentMethod());

    183System.out.println(paypal.getPaymentMethod());184System.out.println(apple.getPaymentMethod());185System.out.println(google.getPaymentMethod());
    outputApple Pay: device-12345
  29. @Override public String getPaymentMethod()

    100@Override101public String getPaymentMethod() {102    return "Google Pay: " + accountIdjane@gmail.com;103}
  30. System.out.println(google.getPaymentMethod());

    184System.out.println(apple.getPaymentMethod());185System.out.println(google.getPaymentMethod());186187System.out.println("\n--- Multiple Sealed Interfaces ---");188Refundable refundable = new CreditCardPayment("1111222233334444");189refundable.refund(25.00);
    outputGoogle Pay: jane@gmail.com
    
    --- Multiple Sealed Interfaces ---
  31. this.cardNumber ← 1111222233334444

    115CreditCardPayment(String cardNumber1111222233334444) {116    this.cardNumber→ 1111222233334444 = cardNumber1111222233334444;117}
  32. refundable ← ⟨CreditCardPayment G⟩

    187System.out.println("\n--- Multiple Sealed Interfaces ---");188Refundable refundable→ ⟨CreditCardPayment G⟩ = new CreditCardPayment("1111222233334444");189refundable.refund(25.00);
  33. @Override public void refund(double amount)

    130@Override131public void refund(double amount25.0) {132    System.out.println("Refunding $" + amount25.0 + " to credit card");133}
    outputRefunding $25.0 to credit card
  34. refundable.refund(25.00);

    188    Refundable refundable = new CreditCardPayment("1111222233334444");189    refundable.refund(25.00);190    191    System.out.println("\n=== Hierarchy ===");192    System.out.println("""193        Payment (sealed interface)194        ├── CreditCard (final class)195        ├── DebitCard (final class)196        └── DigitalWallet (sealed class)197            ├── PayPal (final)198            ├── ApplePay (final)199            └── GooglePay (final)200        201        Refundable (sealed interface)202        ├── CreditCardPayment (final, also implements Payment)203        └── PayPalPayment (final, also implements Payment)204        """);205}
    output
    === Hierarchy ===
    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
Replay: real traced execution (multi-file project)
// 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();
        };
    }
}
  1. public static void main(String[] args)

    99public class PatternMatching {100    public static void main(String[] args) {101        System.out.println("=== Pattern Matching with Sealed Classes ===\n");102        103        // Build expressions //?buildexpr104        Expr simple = new Num(42);105        Expr addition = new Add(new Num(10), new Num(20));
    output=== Pattern Matching with Sealed Classes ===
  2. this.value ← 42

    pass 1 of 8
    14Num(int value42) {15    this.value→ 42 = value42;16}
    All 8 passes — pass 1 is the card above
    passvalueexprleftrightthis.valuethis.exprthis.leftthis.right
    14242
    21010
    32020
    422
    533
    644(2 + 3)(-4)44(2 + 3)(-4)
    7202020
    84040
  3. simple ← 42

    103// Build expressions //?buildexpr104Expr simple→ 42 = new Num(42);105Expr addition = new Add(new Num(10), new Num(20));106Expr complex = new Mul(
  4. this.left ← 10, this.right ← 20

    pass 1 of 3
    37Add(Expr left10, Expr right20) {38    this.left→ 10 = left10;39    this.right→ 20 = right20;40}
    All 3 passes — pass 1 is the card above
    passleftrightexprthis.leftthis.rightthis.expr
    110201020
    2234234
    320402040
  5. addition ← (10 + 20)

    104Expr simple = new Num(42);105Expr addition→ (10 + 20) = new Add(new Num(10), new Num(20));106Expr complex = new Mul(107    new Add(new Num(2), new Num(3)),108    new Neg(new Num(4))109);  // (2 + 3) * (-4) = -20
  6. this.expr ← 4

    82Neg(Expr expr4) {83    this.expr→ 4 = expr4;84}
  7. this.left ← (2 + 3), this.right ← (-4)

    60Mul(Expr left(2 + 3), Expr right(-4)) {61    this.left→ (2 + 3) = left(2 + 3);62    this.right→ (-4) = right(-4);63}
  8. complex ← ((2 + 3) * (-4))

    105Expr addition = new Add(new Num(10), new Num(20));106Expr complex→ ((2 + 3) * (-4)) = new Mul(107    new Add(new Num(2), new Num(3)),108    new Neg(new Num(4))109);  // (2 + 3) * (-4) = -20110111System.out.println("--- Evaluation ---");112System.out.println(simple42 + " = " + simple.eval());113System.out.println(addition + " = " + addition.eval());
    output--- Evaluation ---
  9. @Override public int eval()

    pass 1 of 10
    22@Override23public int eval() {24    return value42;25}
    All 10 passes — pass 1 is the card above
    passvalue
    142
    210
    320
    42
    53
    64
    710
    820
    920
    1040
  10. System.out.println(simple + " = " + simple.eval());

    111System.out.println("--- Evaluation ---");112System.out.println(simple42 + " = " + simple.eval());113System.out.println(addition(10 + 20) + " = " + addition.eval());114System.out.println(complex + " = " + complex.eval());
    output42 = 42
  11. System.out.println(addition + " = " + addition.eval());

    112System.out.println(simple + " = " + simple.eval());113System.out.println(addition(10 + 20) + " = " + addition.eval());114System.out.println(complex((2 + 3) * (-4)) + " = " + complex.eval());
    output(10 + 20) = 30
  12. System.out.println(complex + " = " + complex.eval());

    113System.out.println(addition + " = " + addition.eval());114System.out.println(complex((2 + 3) * (-4)) + " = " + complex.eval());115116// Pattern matching in switch - EXHAUSTIVE! //?exhaustive117System.out.println("\n--- Pattern Matching ---");118describeExpr(simple42);119describeExpr(addition);
    output((2 + 3) * (-4)) = -20
    
    --- Pattern Matching ---
  13. static void describeExpr(Expr expr)

    pass 1 of 3
    139// Pattern matching with exhaustive switch //?describemethod140static void describeExpr(Expr expr42) {141    String description = switch (expr) { //?patternswitch142        case Num n -> "Number: " + n.getValue(); //?numpattern143        case Add a -> "Addition of " + a.getLeft() + " and " + a.getRight(); //?addpattern144        case Mul m -> "Multiplication of " + m.getLeft() + " and " + m.getRight();145        case Neg n -> "Negation of " + n.getExpr();146        // No default needed! Compiler knows all cases covered //?nodefault147    };148    System.out.println(description);
    All 3 passes — pass 1 is the card above
    passexprleftright
    142
    2(10 + 20)1020
    3((2 + 3) * (-4))(2 + 3)(-4)
  14. public int getValue()

    pass 1 of 3
    18public int getValue() {19    return value42;20}
    All 3 passes — pass 1 is the card above
    passvalueright
    142
    21020
    320
  15. description ← Number: 42

    117    System.out.println("\n--- Pattern Matching ---");118    describeExpr(simple42);119    describeExpr(addition(10 + 20));120    describeExpr(complex);121    122    System.out.println("\n--- Transformation ---");123    Expr doubled = transform(addition);124    System.out.println("Original: " + addition + " = " + addition.eval());125    System.out.println("Doubled:  " + doubled + " = " + doubled.eval());126    127    System.out.println("\n=== Why Exhaustive Matters ===");128    System.out.println("""129        With sealed classes, the compiler KNOWS all subclasses.130        Switch can check if all cases are covered!131        132        If we add a new Expr type (e.g., Div):133        - Compiler shows error in every switch134        - Forces us to handle new case135        - No runtime surprises!136        """);137}138139// Pattern matching with exhaustive switch //?describemethod140static void describeExpr(Expr expr) {141    String description→ Number: 42 = switch (expr) { //?patternswitch142        case Num n -> "Number: " + n.getValue(); //?numpattern143        case Add a -> "Addition of " + a.getLeft() + " and " + a.getRight(); //?addpattern144        case Mul m -> "Multiplication of " + m.getLeft() + " and " + m.getRight();145        case Neg n -> "Negation of " + n.getExpr();146        // No default needed! Compiler knows all cases covered //?nodefault147    };148    System.out.println(descriptionNumber: 42);149}
    outputNumber: 42
  16. public Expr getLeft()

    pass 1 of 2
    42public Expr getLeft() { return left10; }43public Expr getRight() { return right; }
  17. public Expr getRight()

    pass 1 of 2
    42public Expr getLeft() { return left; }43public Expr getRight() { return right20; }
  18. description ← Addition of 10 and 20

    118    describeExpr(simple);119    describeExpr(addition(10 + 20));120    describeExpr(complex((2 + 3) * (-4)));121    122    System.out.println("\n--- Transformation ---");123    Expr doubled = transform(addition);124    System.out.println("Original: " + addition + " = " + addition.eval());125    System.out.println("Doubled:  " + doubled + " = " + doubled.eval());126    127    System.out.println("\n=== Why Exhaustive Matters ===");128    System.out.println("""129        With sealed classes, the compiler KNOWS all subclasses.130        Switch can check if all cases are covered!131        132        If we add a new Expr type (e.g., Div):133        - Compiler shows error in every switch134        - Forces us to handle new case135        - No runtime surprises!136        """);137}138139// Pattern matching with exhaustive switch //?describemethod140static void describeExpr(Expr expr) {141    String description→ Addition of 10 and 20 = switch (expr) { //?patternswitch142        case Num n -> "Number: " + n.getValue(); //?numpattern143        case Add a -> "Addition of " + a.getLeft() + " and " + a.getRight(); //?addpattern144        case Mul m -> "Multiplication of " + m.getLeft() + " and " + m.getRight();145        case Neg n -> "Negation of " + n.getExpr();146        // No default needed! Compiler knows all cases covered //?nodefault147    };148    System.out.println(descriptionAddition of 10 and 20);149}
    outputAddition of 10 and 20
  19. public Expr getLeft()

    65public Expr getLeft() { return left(2 + 3); }66public Expr getRight() { return right; }
  20. public Expr getRight()

    65public Expr getLeft() { return left; }66public Expr getRight() { return right(-4); }
  21. description ← Multiplication of (2 + 3) and (-4)

    119    describeExpr(addition);120    describeExpr(complex((2 + 3) * (-4)));121    122    System.out.println("\n--- Transformation ---");123    Expr doubled = transform(addition(10 + 20));124    System.out.println("Original: " + addition + " = " + addition.eval());125    System.out.println("Doubled:  " + doubled + " = " + doubled.eval());126    127    System.out.println("\n=== Why Exhaustive Matters ===");128    System.out.println("""129        With sealed classes, the compiler KNOWS all subclasses.130        Switch can check if all cases are covered!131        132        If we add a new Expr type (e.g., Div):133        - Compiler shows error in every switch134        - Forces us to handle new case135        - No runtime surprises!136        """);137}138139// Pattern matching with exhaustive switch //?describemethod140static void describeExpr(Expr expr) {141    String description→ Multiplication of (2 + 3) and (-4) = switch (expr) { //?patternswitch142        case Num n -> "Number: " + n.getValue(); //?numpattern143        case Add a -> "Addition of " + a.getLeft() + " and " + a.getRight(); //?addpattern144        case Mul m -> "Multiplication of " + m.getLeft() + " and " + m.getRight();145        case Neg n -> "Negation of " + n.getExpr();146        // No default needed! Compiler knows all cases covered //?nodefault147    };148    System.out.println(descriptionMultiplication of (2 + 3) and (-4));149}
    outputMultiplication of (2 + 3) and (-4)
    
    --- Transformation ---
  22. static Expr transform(Expr expr)

    pass 1 of 3
    151// Transform expression using pattern matching //?transformmethod152static Expr transform(Expr expr(10 + 20)) {153    return switch (expr) { //?transformswitch154        case Num n -> new Num(n.getValue() * 2);  // Double numbers155        case Add a -> new Add(transform(a.getLeft()), transform(a.getRight()));156        case Mul m -> new Mul(transform(m.getLeft()), transform(m.getRight()));157        case Neg n -> new Neg(transform(n.getExpr()));158    };159}
    All 3 passes — pass 1 is the card above
    passexprleftright
    1(10 + 20)10
    21020
    320
  23. public Expr getLeft()

    pass 2 of 2
    42public Expr getLeft() { return left10; }43public Expr getRight() { return right; }
  24. public Expr getRight()

    pass 2 of 2
    42public Expr getLeft() { return left; }43public Expr getRight() { return right20; }
  25. doubled ← (20 + 40)

    122System.out.println("\n--- Transformation ---");123Expr doubled→ (20 + 40) = transform(addition(10 + 20));124System.out.println("Original: " + addition(10 + 20) + " = " + addition.eval());125System.out.println("Doubled:  " + doubled + " = " + doubled.eval());
  26. System.out.println("Original: " + addition + " = " + addition.eval());

    123Expr doubled = transform(addition);124System.out.println("Original: " + addition(10 + 20) + " = " + addition.eval());125System.out.println("Doubled:  " + doubled(20 + 40) + " = " + doubled.eval());
    outputOriginal: (10 + 20) = 30
  27. System.out.println("Doubled: " + doubled + " = " + doubled.eval());

    124    System.out.println("Original: " + addition + " = " + addition.eval());125    System.out.println("Doubled:  " + doubled(20 + 40) + " = " + doubled.eval());126    127    System.out.println("\n=== Why Exhaustive Matters ===");128    System.out.println("""129        With sealed classes, the compiler KNOWS all subclasses.130        Switch can check if all cases are covered!131        132        If we add a new Expr type (e.g., Div):133        - Compiler shows error in every switch134        - Forces us to handle new case135        - No runtime surprises!136        """);137}
    outputDoubled:  (20 + 40) = 60
    
    === Why Exhaustive Matters ===
    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!

Switch on sealed type is exhaustive - no default needed.

Records with sealed

Combine records and sealed for algebraic data types.

scaleFactor
RecordsInSealed.java
Replay: real traced execution (multi-file project)
// 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 = 2.0;
        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 = 0.5;
        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 = 3.0;
        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);
        }
    }
}
  1. circle ← Circle[radius=5.0], rect ← Rectangle[width=4.0, height=6.0]

    110public class RecordsInSealed {111    public static void main(String[] args) {112        System.out.println("=== Records with Sealed Classes ===\n");113        114        // Create shapes using records //?createshapes115        Circle circle→ Circle[radius=5.0] = new Circle(5);116        Rectangle rect→ Rectangle[width=4.0, height=6.0] = new Rectangle(4, 6);117        Rectangle square→ Rectangle[width=5.0, height=5.0] = new Rectangle(5, 5);118        Triangle equilateral = new Triangle(3, 3, 3);119        Triangle scalene = new Triangle(3, 4, 5);
    output=== Records with Sealed Classes ===
  2. equilateral ← Triangle[a=3.0, b=3.0, c=3.0]

    117Rectangle square = new Rectangle(5, 5);118Triangle equilateral→ Triangle[a=3.0, b=3.0, c=3.0] = new Triangle(3, 3, 3);119Triangle scalene = new Triangle(3, 4, 5);
  3. scalene ← Triangle[a=3.0, b=4.0, c=5.0]

    118Triangle equilateral = new Triangle(3, 3, 3);119Triangle scalene→ Triangle[a=3.0, b=4.0, c=5.0] = new Triangle(3, 4, 5);120121Shape[] shapes = {circle, rect, square, equilateral, scalene};122123System.out.println("--- Shape Descriptions ---");124for (Shape shape : shapes) {
    output--- Shape Descriptions ---
  4. for (Shape shape : shapes)

    pass 1 of 5
    123System.out.println("--- Shape Descriptions ---");124for (Shape shapeCircle[radius=5.0] : shapes) {125    System.out.println(ShapeProcessor.describe(shapeCircle[radius=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 
    All 5 passes — pass 1 is the card above
    passshape
    1Circle[radius=5.0]
    2Rectangle[width=4.0, height=6.0]
    3Rectangle[width=5.0, height=5.0]
    4Triangle[a=3.0, b=3.0, c=3.0]
    5Triangle[a=3.0, b=4.0, c=5.0]
  5. static String describe(Shape shape)

    pass 1 of 5
    56// Exhaustive switch with record patterns //?recordpatterns57static String describe(Shape shapeCircle[radius=5.0]) {58    return switch (shape) {59        case Circle(var r) -> //?circlepattern60            String.format("Circle with radius %.2f", r);61        case Rectangle(var w, var h) when w == h -> //?squarepattern62            String.format("Square with side %.2f", w);63        case Rectangle(var w, var h) -> //?rectpattern64            String.format("Rectangle %.2f x %.2f", w, h);65        case Triangle(var a, var b, var c) when a == b && b == c -> //?equilateral66            String.format("Equilateral triangle with side %.2f", a);67        case Triangle(var a, var b, var c) -> //?anytrianglepattern68            String.format("Triangle with sides %.2f, %.2f, %.2f", a, b, c);69    };70}
    All 5 passes — pass 1 is the card above
    passshape
    1Circle[radius=5.0]
    2Rectangle[width=4.0, height=6.0]
    3Rectangle[width=5.0, height=5.0]
    4Triangle[a=3.0, b=3.0, c=3.0]
    5Triangle[a=3.0, b=4.0, c=5.0]
  6. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeCircle[radius=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputCircle with radius 5.00
  7. @Override public double area()

    pass 1 of 2
    10record Circle(double radius) implements Shape { //?circlerecord11    @Override12    public double area() {13        return Math.PI * radius5.0 * radius;14    }
  8. @Override public double perimeter()

    16@Override17public double perimeter() {18    return 2 * Math.PI * radius5.0;19}
  9. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  10. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeRectangle[width=4.0, height=6.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputRectangle 4.00 x 6.00
  11. @Override public double area()

    pass 1 of 4
    22record Rectangle(double width, double height) implements Shape { //?rectanglerecord23    @Override24    public double area() {25        return width4.0 * height6.0;26    }
    All 4 passes — pass 1 is the card above
    passwidthheight
    14.06.0
    25.05.0
    34.06.0
    45.05.0
  12. @Override public double perimeter()

    pass 1 of 2
    28@Override29public double perimeter() {30    return 2 * (width4.0 + height6.0);31}
  13. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  14. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeRectangle[width=5.0, height=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputSquare with side 5.00
  15. @Override public double perimeter()

    pass 2 of 2
    28@Override29public double perimeter() {30    return 2 * (width5.0 + height5.0);31}
  16. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  17. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeTriangle[a=3.0, b=3.0, c=3.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputEquilateral triangle with side 3.00
  18. s ← 4.5

    pass 1 of 4
    42@Override43public double area() {44    double s→ 4.5 = (a3.0 + b3.0 + c3.0) / 2;45    return Math.sqrt(s4.5 * (s - a3.0) * (s - b3.0) * (s - c3.0));46}
    All 4 passes — pass 1 is the card above
    passbcs
    13.03.04.5
    24.05.06.0
    33.03.04.5
    44.05.06.0
  19. @Override public double perimeter()

    pass 1 of 2
    48@Override49public double perimeter() {50    return a3.0 + b3.0 + c3.0;51}
  20. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  21. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeTriangle[a=3.0, b=4.0, c=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputTriangle with sides 3.00, 4.00, 5.00
  22. @Override public double perimeter()

    pass 2 of 2
    48@Override49public double perimeter() {50    return a3.0 + b4.0 + c5.0;51}
  23. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  24. System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));

    130System.out.println("\n--- Total Area ---");131System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));
    output
    --- Total Area ---
  25. total ← 0.0

    72// Calculate total area //?totalarea73static double totalArea(Shape... shapes) {74    double total→ 0.0 = 0;75    for (Shape shape : shapes) {
  26. for (Shape shape : shapes)

    pass 1 of 5
    74double total = 0;75for (Shape shapeCircle[radius=5.0] : shapes) {76    total0.0 += shape.area();77}
    All 5 passes — pass 1 is the card above
    passshapetotalradius
    1Circle[radius=5.0]0.05.0
    2Rectangle[width=4.0, height=6.0]78.53981633974483
    3Rectangle[width=5.0, height=5.0]102.53981633974483
    4Triangle[a=3.0, b=3.0, c=3.0]127.53981633974483
    5Triangle[a=3.0, b=4.0, c=5.0]131.4369306567748
  27. @Override public double area()

    pass 2 of 2
    10record Circle(double radius) implements Shape { //?circlerecord11    @Override12    public double area() {13        return Math.PI * radius5.0 * radius;14    }
  28. total ← 78.53981633974483

    75for (Shape shape : shapes) {76    total→ 78.53981633974483 += shape.area();77}
  29. total ← 102.53981633974483

    75for (Shape shape : shapes) {76    total→ 102.53981633974483 += shape.area();77}
  30. total ← 127.53981633974483

    75for (Shape shape : shapes) {76    total→ 127.53981633974483 += shape.area();77}
  31. total ← 131.4369306567748

    75for (Shape shape : shapes) {76    total→ 131.4369306567748 += shape.area();77}
  32. total ← 137.4369306567748

    75for (Shape shape : shapes) {76    total→ 137.4369306567748 += shape.area();77}
  33. return total;

    77    }78    return total137.4369306567748;79}
  34. scaleFactor ← 2.0

    130System.out.println("\n--- Total Area ---");131System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));132133// Scaling //?testscale134System.out.println("\n--- Scaling ---");135double scaleFactor→ 2.0 = 2.0;  //@scaleFactor=2.0, 0.5, 3.0136Shape scaledCircle = ShapeProcessor.scale(circleCircle[radius=5.0], scaleFactor2.0);137System.out.println("Original: " + circle);
    output
    --- Scaling ---
  35. static Shape scale(Shape shape, double factor)

    81// Scale shape //?scale82static Shape scale(Shape shapeCircle[radius=5.0], double factor2.0) {83    return switch (shape) {84        case Circle(var r) -> new Circle(r * factor);85        case Rectangle(var w, var h) -> new Rectangle(w * factor, h * factor);86        case Triangle(var a, var b, var c) -> new Triangle(a * factor, b * factor, c * factor);87    };88}
  36. scaledCircle ← Circle[radius=10.0], success ← Success[value=42]

    135double scaleFactor = 2.0;  //@scaleFactor=2.0, 0.5, 3.0136Shape scaledCircle→ Circle[radius=10.0] = ShapeProcessor.scale(circleCircle[radius=5.0], scaleFactor2.0);137System.out.println("Original: " + circleCircle[radius=5.0]);138System.out.println("Scaled " + scaleFactor2.0 + "x: " + scaledCircleCircle[radius=10.0]);139140// Result type example //?testresult141System.out.println("\n--- Result Type ---");142Result<Integer> success→ Success[value=42] = new Success<>(42);143Result<Integer> failure→ Failure[error=Division by zero] = new Failure<>("Division by zero");144145processResult(successSuccess[value=42]);146processResult(failure);
    outputOriginal: Circle[radius=5.0]
    Scaled 2.0x: Circle[radius=10.0]
    
    --- Result Type ---
  37. static void processResult(Result<Integer> result)

    pass 1 of 2
    145    processResult(successSuccess[value=42]);146    processResult(failureFailure[error=Division by zero]);147    148    System.out.println("\n=== Benefits ===");149    System.out.println("""150        Records + Sealed:151        1. Records are implicitly final (perfect for sealed permits)152        2. Record patterns enable deconstruction in switch153        3. Compact syntax for value objects154        4. Automatic equals/hashCode/toString155        5. Guards can add extra conditions156        157        Common patterns:158        - Shape hierarchies159        - Expression trees (AST)160        - Result/Either types161        - Event types162        - Command patterns163        """);164}165166// Process result with pattern matching //?processresult167static void processResult(Result<Integer> resultSuccess[value=42]) {168    switch (result) {
  38. static void processResult(Result<Integer> result)

    pass 2 of 2
    145    processResult(success);146    processResult(failureFailure[error=Division by zero]);147    148    System.out.println("\n=== Benefits ===");149    System.out.println("""150        Records + Sealed:151        1. Records are implicitly final (perfect for sealed permits)152        2. Record patterns enable deconstruction in switch153        3. Compact syntax for value objects154        4. Automatic equals/hashCode/toString155        5. Guards can add extra conditions156        157        Common patterns:158        - Shape hierarchies159        - Expression trees (AST)160        - Result/Either types161        - Event types162        - Command patterns163        """);164}165166// Process result with pattern matching //?processresult167static void processResult(Result<Integer> resultFailure[error=Division by zero]) {168    switch (result) {
    output
    === Benefits ===
    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
  1. circle ← Circle[radius=5.0], rect ← Rectangle[width=4.0, height=6.0]

    110public class RecordsInSealed {111    public static void main(String[] args) {112        System.out.println("=== Records with Sealed Classes ===\n");113        114        // Create shapes using records115        Circle circle→ Circle[radius=5.0] = new Circle(5);116        Rectangle rect→ Rectangle[width=4.0, height=6.0] = new Rectangle(4, 6);117        Rectangle square→ Rectangle[width=5.0, height=5.0] = new Rectangle(5, 5);118        Triangle equilateral = new Triangle(3, 3, 3);119        Triangle scalene = new Triangle(3, 4, 5);
    output=== Records with Sealed Classes ===
  2. equilateral ← Triangle[a=3.0, b=3.0, c=3.0]

    117Rectangle square = new Rectangle(5, 5);118Triangle equilateral→ Triangle[a=3.0, b=3.0, c=3.0] = new Triangle(3, 3, 3);119Triangle scalene = new Triangle(3, 4, 5);
  3. scalene ← Triangle[a=3.0, b=4.0, c=5.0]

    118Triangle equilateral = new Triangle(3, 3, 3);119Triangle scalene→ Triangle[a=3.0, b=4.0, c=5.0] = new Triangle(3, 4, 5);120121Shape[] shapes = {circle, rect, square, equilateral, scalene};122123System.out.println("--- Shape Descriptions ---");124for (Shape shape : shapes) {
    output--- Shape Descriptions ---
  4. for (Shape shape : shapes)

    pass 1 of 5
    123System.out.println("--- Shape Descriptions ---");124for (Shape shapeCircle[radius=5.0] : shapes) {125    System.out.println(ShapeProcessor.describe(shapeCircle[radius=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 
    All 5 passes — pass 1 is the card above
    passshape
    1Circle[radius=5.0]
    2Rectangle[width=4.0, height=6.0]
    3Rectangle[width=5.0, height=5.0]
    4Triangle[a=3.0, b=3.0, c=3.0]
    5Triangle[a=3.0, b=4.0, c=5.0]
  5. static String describe(Shape shape)

    pass 1 of 5
    56// Exhaustive switch with record patterns57static String describe(Shape shapeCircle[radius=5.0]) {58    return switch (shape) {59        case Circle(var r) ->60            String.format("Circle with radius %.2f", r);61        case Rectangle(var w, var h) when w == h ->62            String.format("Square with side %.2f", w);63        case Rectangle(var w, var h) ->64            String.format("Rectangle %.2f x %.2f", w, h);65        case Triangle(var a, var b, var c) when a == b && b == c ->66            String.format("Equilateral triangle with side %.2f", a);67        case Triangle(var a, var b, var c) ->68            String.format("Triangle with sides %.2f, %.2f, %.2f", a, b, c);69    };70}
    All 5 passes — pass 1 is the card above
    passshape
    1Circle[radius=5.0]
    2Rectangle[width=4.0, height=6.0]
    3Rectangle[width=5.0, height=5.0]
    4Triangle[a=3.0, b=3.0, c=3.0]
    5Triangle[a=3.0, b=4.0, c=5.0]
  6. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeCircle[radius=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputCircle with radius 5.00
  7. @Override public double area()

    pass 1 of 2
    10record Circle(double radius) implements Shape {11    @Override12    public double area() {13        return Math.PI * radius5.0 * radius;14    }
  8. @Override public double perimeter()

    16@Override17public double perimeter() {18    return 2 * Math.PI * radius5.0;19}
  9. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  10. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeRectangle[width=4.0, height=6.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputRectangle 4.00 x 6.00
  11. @Override public double area()

    pass 1 of 4
    22record Rectangle(double width, double height) implements Shape {23    @Override24    public double area() {25        return width4.0 * height6.0;26    }
    All 4 passes — pass 1 is the card above
    passwidthheight
    14.06.0
    25.05.0
    34.06.0
    45.05.0
  12. @Override public double perimeter()

    pass 1 of 2
    28@Override29public double perimeter() {30    return 2 * (width4.0 + height6.0);31}
  13. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  14. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeRectangle[width=5.0, height=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputSquare with side 5.00
  15. @Override public double perimeter()

    pass 2 of 2
    28@Override29public double perimeter() {30    return 2 * (width5.0 + height5.0);31}
  16. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  17. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeTriangle[a=3.0, b=3.0, c=3.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputEquilateral triangle with side 3.00
  18. s ← 4.5

    pass 1 of 4
    42@Override43public double area() {44    double s→ 4.5 = (a3.0 + b3.0 + c3.0) / 2;45    return Math.sqrt(s4.5 * (s - a3.0) * (s - b3.0) * (s - c3.0));46}
    All 4 passes — pass 1 is the card above
    passbcs
    13.03.04.5
    24.05.06.0
    33.03.04.5
    44.05.06.0
  19. @Override public double perimeter()

    pass 1 of 2
    48@Override49public double perimeter() {50    return a3.0 + b3.0 + c3.0;51}
  20. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  21. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeTriangle[a=3.0, b=4.0, c=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputTriangle with sides 3.00, 4.00, 5.00
  22. @Override public double perimeter()

    pass 2 of 2
    48@Override49public double perimeter() {50    return a3.0 + b4.0 + c5.0;51}
  23. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  24. System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));

    130System.out.println("\n--- Total Area ---");131System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));
    output
    --- Total Area ---
  25. total ← 0.0

    72// Calculate total area73static double totalArea(Shape... shapes) {74    double total→ 0.0 = 0;75    for (Shape shape : shapes) {
  26. for (Shape shape : shapes)

    pass 1 of 5
    74double total = 0;75for (Shape shapeCircle[radius=5.0] : shapes) {76    total0.0 += shape.area();77}
    All 5 passes — pass 1 is the card above
    passshapetotalradius
    1Circle[radius=5.0]0.05.0
    2Rectangle[width=4.0, height=6.0]78.53981633974483
    3Rectangle[width=5.0, height=5.0]102.53981633974483
    4Triangle[a=3.0, b=3.0, c=3.0]127.53981633974483
    5Triangle[a=3.0, b=4.0, c=5.0]131.4369306567748
  27. @Override public double area()

    pass 2 of 2
    10record Circle(double radius) implements Shape {11    @Override12    public double area() {13        return Math.PI * radius5.0 * radius;14    }
  28. total ← 78.53981633974483

    75for (Shape shape : shapes) {76    total→ 78.53981633974483 += shape.area();77}
  29. total ← 102.53981633974483

    75for (Shape shape : shapes) {76    total→ 102.53981633974483 += shape.area();77}
  30. total ← 127.53981633974483

    75for (Shape shape : shapes) {76    total→ 127.53981633974483 += shape.area();77}
  31. total ← 131.4369306567748

    75for (Shape shape : shapes) {76    total→ 131.4369306567748 += shape.area();77}
  32. total ← 137.4369306567748

    75for (Shape shape : shapes) {76    total→ 137.4369306567748 += shape.area();77}
  33. return total;

    77    }78    return total137.4369306567748;79}
  34. scaleFactor ← 0.5

    130System.out.println("\n--- Total Area ---");131System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));132133// Scaling134System.out.println("\n--- Scaling ---");135double scaleFactor→ 0.5 = 0.5;136Shape scaledCircle = ShapeProcessor.scale(circleCircle[radius=5.0], scaleFactor0.5);137System.out.println("Original: " + circle);
    output
    --- Scaling ---
  35. static Shape scale(Shape shape, double factor)

    81// Scale shape82static Shape scale(Shape shapeCircle[radius=5.0], double factor0.5) {83    return switch (shape) {84        case Circle(var r) -> new Circle(r * factor);85        case Rectangle(var w, var h) -> new Rectangle(w * factor, h * factor);86        case Triangle(var a, var b, var c) -> new Triangle(a * factor, b * factor, c * factor);87    };88}
  36. scaledCircle ← Circle[radius=2.5], success ← Success[value=42]

    135double scaleFactor = 0.5;136Shape scaledCircle→ Circle[radius=2.5] = ShapeProcessor.scale(circleCircle[radius=5.0], scaleFactor0.5);137System.out.println("Original: " + circleCircle[radius=5.0]);138System.out.println("Scaled " + scaleFactor0.5 + "x: " + scaledCircleCircle[radius=2.5]);139140// Result type example141System.out.println("\n--- Result Type ---");142Result<Integer> success→ Success[value=42] = new Success<>(42);143Result<Integer> failure→ Failure[error=Division by zero] = new Failure<>("Division by zero");144145processResult(successSuccess[value=42]);146processResult(failure);
    outputOriginal: Circle[radius=5.0]
    Scaled 0.5x: Circle[radius=2.5]
    
    --- Result Type ---
  37. static void processResult(Result<Integer> result)

    pass 1 of 2
    145    processResult(successSuccess[value=42]);146    processResult(failureFailure[error=Division by zero]);147    148    System.out.println("\n=== Benefits ===");149    System.out.println("""150        Records + Sealed:151        1. Records are implicitly final (perfect for sealed permits)152        2. Record patterns enable deconstruction in switch153        3. Compact syntax for value objects154        4. Automatic equals/hashCode/toString155        5. Guards can add extra conditions156        157        Common patterns:158        - Shape hierarchies159        - Expression trees (AST)160        - Result/Either types161        - Event types162        - Command patterns163        """);164}165166// Process result with pattern matching167static void processResult(Result<Integer> resultSuccess[value=42]) {168    switch (result) {
  38. static void processResult(Result<Integer> result)

    pass 2 of 2
    145    processResult(success);146    processResult(failureFailure[error=Division by zero]);147    148    System.out.println("\n=== Benefits ===");149    System.out.println("""150        Records + Sealed:151        1. Records are implicitly final (perfect for sealed permits)152        2. Record patterns enable deconstruction in switch153        3. Compact syntax for value objects154        4. Automatic equals/hashCode/toString155        5. Guards can add extra conditions156        157        Common patterns:158        - Shape hierarchies159        - Expression trees (AST)160        - Result/Either types161        - Event types162        - Command patterns163        """);164}165166// Process result with pattern matching167static void processResult(Result<Integer> resultFailure[error=Division by zero]) {168    switch (result) {
    output
    === Benefits ===
    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
  1. circle ← Circle[radius=5.0], rect ← Rectangle[width=4.0, height=6.0]

    110public class RecordsInSealed {111    public static void main(String[] args) {112        System.out.println("=== Records with Sealed Classes ===\n");113        114        // Create shapes using records115        Circle circle→ Circle[radius=5.0] = new Circle(5);116        Rectangle rect→ Rectangle[width=4.0, height=6.0] = new Rectangle(4, 6);117        Rectangle square→ Rectangle[width=5.0, height=5.0] = new Rectangle(5, 5);118        Triangle equilateral = new Triangle(3, 3, 3);119        Triangle scalene = new Triangle(3, 4, 5);
    output=== Records with Sealed Classes ===
  2. equilateral ← Triangle[a=3.0, b=3.0, c=3.0]

    117Rectangle square = new Rectangle(5, 5);118Triangle equilateral→ Triangle[a=3.0, b=3.0, c=3.0] = new Triangle(3, 3, 3);119Triangle scalene = new Triangle(3, 4, 5);
  3. scalene ← Triangle[a=3.0, b=4.0, c=5.0]

    118Triangle equilateral = new Triangle(3, 3, 3);119Triangle scalene→ Triangle[a=3.0, b=4.0, c=5.0] = new Triangle(3, 4, 5);120121Shape[] shapes = {circle, rect, square, equilateral, scalene};122123System.out.println("--- Shape Descriptions ---");124for (Shape shape : shapes) {
    output--- Shape Descriptions ---
  4. for (Shape shape : shapes)

    pass 1 of 5
    123System.out.println("--- Shape Descriptions ---");124for (Shape shapeCircle[radius=5.0] : shapes) {125    System.out.println(ShapeProcessor.describe(shapeCircle[radius=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 
    All 5 passes — pass 1 is the card above
    passshape
    1Circle[radius=5.0]
    2Rectangle[width=4.0, height=6.0]
    3Rectangle[width=5.0, height=5.0]
    4Triangle[a=3.0, b=3.0, c=3.0]
    5Triangle[a=3.0, b=4.0, c=5.0]
  5. static String describe(Shape shape)

    pass 1 of 5
    56// Exhaustive switch with record patterns57static String describe(Shape shapeCircle[radius=5.0]) {58    return switch (shape) {59        case Circle(var r) ->60            String.format("Circle with radius %.2f", r);61        case Rectangle(var w, var h) when w == h ->62            String.format("Square with side %.2f", w);63        case Rectangle(var w, var h) ->64            String.format("Rectangle %.2f x %.2f", w, h);65        case Triangle(var a, var b, var c) when a == b && b == c ->66            String.format("Equilateral triangle with side %.2f", a);67        case Triangle(var a, var b, var c) ->68            String.format("Triangle with sides %.2f, %.2f, %.2f", a, b, c);69    };70}
    All 5 passes — pass 1 is the card above
    passshape
    1Circle[radius=5.0]
    2Rectangle[width=4.0, height=6.0]
    3Rectangle[width=5.0, height=5.0]
    4Triangle[a=3.0, b=3.0, c=3.0]
    5Triangle[a=3.0, b=4.0, c=5.0]
  6. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeCircle[radius=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputCircle with radius 5.00
  7. @Override public double area()

    pass 1 of 2
    10record Circle(double radius) implements Shape {11    @Override12    public double area() {13        return Math.PI * radius5.0 * radius;14    }
  8. @Override public double perimeter()

    16@Override17public double perimeter() {18    return 2 * Math.PI * radius5.0;19}
  9. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  10. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeRectangle[width=4.0, height=6.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputRectangle 4.00 x 6.00
  11. @Override public double area()

    pass 1 of 4
    22record Rectangle(double width, double height) implements Shape {23    @Override24    public double area() {25        return width4.0 * height6.0;26    }
    All 4 passes — pass 1 is the card above
    passwidthheight
    14.06.0
    25.05.0
    34.06.0
    45.05.0
  12. @Override public double perimeter()

    pass 1 of 2
    28@Override29public double perimeter() {30    return 2 * (width4.0 + height6.0);31}
  13. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  14. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeRectangle[width=5.0, height=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputSquare with side 5.00
  15. @Override public double perimeter()

    pass 2 of 2
    28@Override29public double perimeter() {30    return 2 * (width5.0 + height5.0);31}
  16. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  17. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeTriangle[a=3.0, b=3.0, c=3.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputEquilateral triangle with side 3.00
  18. s ← 4.5

    pass 1 of 4
    42@Override43public double area() {44    double s→ 4.5 = (a3.0 + b3.0 + c3.0) / 2;45    return Math.sqrt(s4.5 * (s - a3.0) * (s - b3.0) * (s - c3.0));46}
    All 4 passes — pass 1 is the card above
    passbcs
    13.03.04.5
    24.05.06.0
    33.03.04.5
    44.05.06.0
  19. @Override public double perimeter()

    pass 1 of 2
    48@Override49public double perimeter() {50    return a3.0 + b3.0 + c3.0;51}
  20. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  21. System.out.println(ShapeProcessor.describe(shape));

    124for (Shape shape : shapes) {125    System.out.println(ShapeProcessor.describe(shapeTriangle[a=3.0, b=4.0, c=5.0]));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
    outputTriangle with sides 3.00, 4.00, 5.00
  22. @Override public double perimeter()

    pass 2 of 2
    48@Override49public double perimeter() {50    return a3.0 + b4.0 + c5.0;51}
  23. System.out.printf(" Area: %.2f, Perimeter: %.2f%n",

    125    System.out.println(ShapeProcessor.describe(shape));126    System.out.printf("  Area: %.2f, Perimeter: %.2f%n", 127        shape.area(), shape.perimeter());128}
  24. System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));

    130System.out.println("\n--- Total Area ---");131System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));
    output
    --- Total Area ---
  25. total ← 0.0

    72// Calculate total area73static double totalArea(Shape... shapes) {74    double total→ 0.0 = 0;75    for (Shape shape : shapes) {
  26. for (Shape shape : shapes)

    pass 1 of 5
    74double total = 0;75for (Shape shapeCircle[radius=5.0] : shapes) {76    total0.0 += shape.area();77}
    All 5 passes — pass 1 is the card above
    passshapetotalradius
    1Circle[radius=5.0]0.05.0
    2Rectangle[width=4.0, height=6.0]78.53981633974483
    3Rectangle[width=5.0, height=5.0]102.53981633974483
    4Triangle[a=3.0, b=3.0, c=3.0]127.53981633974483
    5Triangle[a=3.0, b=4.0, c=5.0]131.4369306567748
  27. @Override public double area()

    pass 2 of 2
    10record Circle(double radius) implements Shape {11    @Override12    public double area() {13        return Math.PI * radius5.0 * radius;14    }
  28. total ← 78.53981633974483

    75for (Shape shape : shapes) {76    total→ 78.53981633974483 += shape.area();77}
  29. total ← 102.53981633974483

    75for (Shape shape : shapes) {76    total→ 102.53981633974483 += shape.area();77}
  30. total ← 127.53981633974483

    75for (Shape shape : shapes) {76    total→ 127.53981633974483 += shape.area();77}
  31. total ← 131.4369306567748

    75for (Shape shape : shapes) {76    total→ 131.4369306567748 += shape.area();77}
  32. total ← 137.4369306567748

    75for (Shape shape : shapes) {76    total→ 137.4369306567748 += shape.area();77}
  33. return total;

    77    }78    return total137.4369306567748;79}
  34. scaleFactor ← 3.0

    130System.out.println("\n--- Total Area ---");131System.out.printf("Total: %.2f%n", ShapeProcessor.totalArea(shapes));132133// Scaling134System.out.println("\n--- Scaling ---");135double scaleFactor→ 3.0 = 3.0;136Shape scaledCircle = ShapeProcessor.scale(circleCircle[radius=5.0], scaleFactor3.0);137System.out.println("Original: " + circle);
    output
    --- Scaling ---
  35. static Shape scale(Shape shape, double factor)

    81// Scale shape82static Shape scale(Shape shapeCircle[radius=5.0], double factor3.0) {83    return switch (shape) {84        case Circle(var r) -> new Circle(r * factor);85        case Rectangle(var w, var h) -> new Rectangle(w * factor, h * factor);86        case Triangle(var a, var b, var c) -> new Triangle(a * factor, b * factor, c * factor);87    };88}
  36. scaledCircle ← Circle[radius=15.0], success ← Success[value=42]

    135double scaleFactor = 3.0;136Shape scaledCircle→ Circle[radius=15.0] = ShapeProcessor.scale(circleCircle[radius=5.0], scaleFactor3.0);137System.out.println("Original: " + circleCircle[radius=5.0]);138System.out.println("Scaled " + scaleFactor3.0 + "x: " + scaledCircleCircle[radius=15.0]);139140// Result type example141System.out.println("\n--- Result Type ---");142Result<Integer> success→ Success[value=42] = new Success<>(42);143Result<Integer> failure→ Failure[error=Division by zero] = new Failure<>("Division by zero");144145processResult(successSuccess[value=42]);146processResult(failure);
    outputOriginal: Circle[radius=5.0]
    Scaled 3.0x: Circle[radius=15.0]
    
    --- Result Type ---
  37. static void processResult(Result<Integer> result)

    pass 1 of 2
    145    processResult(successSuccess[value=42]);146    processResult(failureFailure[error=Division by zero]);147    148    System.out.println("\n=== Benefits ===");149    System.out.println("""150        Records + Sealed:151        1. Records are implicitly final (perfect for sealed permits)152        2. Record patterns enable deconstruction in switch153        3. Compact syntax for value objects154        4. Automatic equals/hashCode/toString155        5. Guards can add extra conditions156        157        Common patterns:158        - Shape hierarchies159        - Expression trees (AST)160        - Result/Either types161        - Event types162        - Command patterns163        """);164}165166// Process result with pattern matching167static void processResult(Result<Integer> resultSuccess[value=42]) {168    switch (result) {
  38. static void processResult(Result<Integer> result)

    pass 2 of 2
    145    processResult(success);146    processResult(failureFailure[error=Division by zero]);147    148    System.out.println("\n=== Benefits ===");149    System.out.println("""150        Records + Sealed:151        1. Records are implicitly final (perfect for sealed permits)152        2. Record patterns enable deconstruction in switch153        3. Compact syntax for value objects154        4. Automatic equals/hashCode/toString155        5. Guards can add extra conditions156        157        Common patterns:158        - Shape hierarchies159        - Expression trees (AST)160        - Result/Either types161        - Event types162        - Command patterns163        """);164}165166// Process result with pattern matching167static void processResult(Result<Integer> resultFailure[error=Division by zero]) {168    switch (result) {
    output
    === Benefits ===
    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

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

Exercise: Practical.java

Model a state machine with sealed classes