OOP Advanced
Sealed Classes
Controlled Inheritance
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.
// 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'
""");
}
}
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 ===this.name ← Buddy
pass 1 of 37Animal(String nameBuddy) {8 this.name→ Buddy = nameBuddy;9}All 3 passes — pass 1 is the card above pass namethis.name1 Buddy Buddy 2 Whiskers Whiskers 3 Tweety Tweety Dog(String name)
21final class Dog extends Animal { //?finalsubclass22 Dog(String nameBuddy) {23 super(name);dog ← ⟨Dog A⟩
58// Create permitted subclasses //?createobjects59Dog dog→ ⟨Dog A⟩ = new Dog("Buddy");60Cat cat = new Cat("Whiskers");61Bird bird = new Bird("Tweety");Cat(String name)
31final class Cat extends Animal { //?anothersubclass32 Cat(String nameWhiskers) {33 super(name);cat ← ⟨Cat B⟩
59Dog dog = new Dog("Buddy");60Cat cat→ ⟨Cat B⟩ = new Cat("Whiskers");61Bird bird = new Bird("Tweety");Bird(String name)
41final class Bird extends Animal {42 Bird(String nameTweety) {43 super(name);bird ← ⟨Bird C⟩
60Cat cat = new Cat("Whiskers");61Bird bird→ ⟨Bird C⟩ = new Bird("Tweety");6263dog.describe();64dog.bark();public void describe()
pass 1 of 615public void describe() {16 System.out.println("I am " + nameBuddy);17}outputI am BuddyAll 6 passes — pass 1 is the card above pass name1 Buddy 2 Whiskers 3 Tweety 4 Buddy 5 Whiskers 6 Tweety dog.describe();
63dog.describe();64dog.bark();public String getName()
pass 1 of 311public String getName() {12 return nameBuddy;13}All 3 passes — pass 1 is the card above pass name1 Buddy 2 Whiskers 3 Tweety System.out.println(getName() + " says: Woof!");
26public void bark() {27 System.out.println(getName() + " says: Woof!");28}outputBuddy says: Woof!dog.bark();
63dog.describe();64dog.bark();6566System.out.println();67cat.describe();68cat.meow();cat.describe();
66System.out.println();67cat.describe();68cat.meow();System.out.println(getName() + " says: Meow!");
36public void meow() {37 System.out.println(getName() + " says: Meow!");38}outputWhiskers says: Meow!cat.meow();
67cat.describe();68cat.meow();6970System.out.println();71bird.describe();72bird.chirp();bird.describe();
70System.out.println();71bird.describe();72bird.chirp();System.out.println(getName() + " says: Chirp!");
46public void chirp() {47 System.out.println(getName() + " says: Chirp!");48}outputTweety says: Chirp!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 ===for (Animal animal : animals)
pass 1 of 376Animal[] animals = {dog, cat, bird}; //?polymorphism77for (Animal animal⟨Dog A⟩ : animals) {78 animal.describe();79}All 3 passes — pass 1 is the card above pass animal1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Bird C⟩ animal.describe();
77for (Animal animal : animals) {78 animal.describe();79}animal.describe();
77for (Animal animal : animals) {78 animal.describe();79}animal.describe();
77for (Animal animal : animals) {78 animal.describe();79}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.
Permitted subclass options
Subclasses must be final, sealed, or non-sealed.
// 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
""");
}
}
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):this.brand ← Harley
pass 1 of 87Vehicle(String brandHarley) {8 this.brand→ Harley = brandHarley;9}All 8 passes — pass 1 is the card above pass brandthis.brand1 Harley Harley 2 Toyota Toyota 3 Jeep Jeep 4 Porsche Porsche 5 Ford Ford 6 Chevrolet Chevrolet 7 Peterbilt Peterbilt 8 BigFoot BigFoot Motorcycle(String brand)
17final class Motorcycle extends Vehicle { //?motorcyclefinal18 Motorcycle(String brandHarley) {19 super(brand);harley ← ⟨Motorcycle A⟩
93System.out.println("1. FINAL (Motorcycle):");94Motorcycle harley→ ⟨Motorcycle A⟩ = new Motorcycle("Harley");95harley.wheelie();96// Cannot create subclass of Motorcyclepublic String getBrand()
pass 1 of 811public String getBrand() {12 return brandHarley;13}All 8 passes — pass 1 is the card above pass brand1 Harley 2 Toyota 3 Jeep 4 Porsche 5 Ford 6 Chevrolet 7 Peterbilt 8 BigFoot 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!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):Car(String brand)
pass 1 of 329sealed class Car extends Vehicle permits Sedan, SUV, SportsCar { //?carpermits30 Car(String brandToyota) {31 super(brand);All 3 passes — pass 1 is the card above pass brand1 Toyota 2 Jeep 3 Porsche Sedan(String brand)
40final class Sedan extends Car { //?sedanfinal41 Sedan(String brandToyota) {42 super(brand);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");SUV(String brand)
46final class SUV extends Car {47 SUV(String brandJeep) {48 super(brand);jeep ← ⟨SUV C⟩
99Sedan toyota = new Sedan("Toyota");100SUV jeep→ ⟨SUV C⟩ = new SUV("Jeep");101SportsCar porsche = new SportsCar("Porsche");102toyota.honk();SportsCar(String brand)
52final class SportsCar extends Car {53 SportsCar(String brandPorsche) {54 super(brand);porsche ← ⟨SportsCar D⟩
100SUV jeep = new SUV("Jeep");101SportsCar porsche→ ⟨SportsCar D⟩ = new SportsCar("Porsche");102toyota.honk();103jeep.honk();System.out.println(getBrand() + " car: Beep beep!");
34public void honk() {35 System.out.println(getBrand() + " car: Beep beep!");36}outputToyota car: Beep beep!toyota.honk();
101SportsCar porsche = new SportsCar("Porsche");102toyota.honk();103jeep.honk();104porsche.honk();System.out.println(getBrand() + " car: Beep beep!");
34public void honk() {35 System.out.println(getBrand() + " car: Beep beep!");36}outputJeep car: Beep beep!jeep.honk();
102toyota.honk();103jeep.honk();104porsche.honk();105// Car hierarchy is closed: only Sedan, SUV, SportsCarSystem.out.println(getBrand() + " car: Beep beep!");
34public void honk() {35 System.out.println(getBrand() + " car: Beep beep!");36}outputPorsche car: Beep beep!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):Truck(String brand)
pass 1 of 459non-sealed class Truck extends Vehicle { //?trucknonseal60 Truck(String brandFord) {61 super(brand);All 4 passes — pass 1 is the card above pass brand1 Ford 2 Chevrolet 3 Peterbilt 4 BigFoot 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");PickupTruck(String brand)
pass 1 of 270class PickupTruck extends Truck { //?pickup71 PickupTruck(String brandChevrolet) {72 super(brand);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");SemiTruck(String brand)
76class SemiTruck extends Truck { //?semi77 SemiTruck(String brandPeterbilt) {78 super(brand);semi ← ⟨SemiTruck G⟩
109PickupTruck pickup = new PickupTruck("Chevrolet");110SemiTruck semi→ ⟨SemiTruck G⟩ = new SemiTruck("Peterbilt");111MonsterTruck monster = new MonsterTruck("BigFoot");112ford.loadCargo();PickupTruck(String brand)
pass 2 of 270class PickupTruck extends Truck { //?pickup71 PickupTruck(String brandBigFoot) {72 super(brand);MonsterTruck(String brand)
83class MonsterTruck extends PickupTruck {84 MonsterTruck(String brandBigFoot) {85 super(brand);monster ← ⟨MonsterTruck H⟩
110SemiTruck semi = new SemiTruck("Peterbilt");111MonsterTruck monster→ ⟨MonsterTruck H⟩ = new MonsterTruck("BigFoot");112ford.loadCargo();113pickup.loadCargo();System.out.println(getBrand() + " truck loading cargo");
64public void loadCargo() {65 System.out.println(getBrand() + " truck loading cargo");66}outputFord truck loading cargoford.loadCargo();
111MonsterTruck monster = new MonsterTruck("BigFoot");112ford.loadCargo();113pickup.loadCargo();114semi.loadCargo();System.out.println(getBrand() + " truck loading cargo");
64public void loadCargo() {65 System.out.println(getBrand() + " truck loading cargo");66}outputChevrolet truck loading cargopickup.loadCargo();
112ford.loadCargo();113pickup.loadCargo();114semi.loadCargo();115monster.loadCargo();System.out.println(getBrand() + " truck loading cargo");
64public void loadCargo() {65 System.out.println(getBrand() + " truck loading cargo");66}outputPeterbilt truck loading cargosemi.loadCargo();
113pickup.loadCargo();114semi.loadCargo();115monster.loadCargo();116// Truck hierarchy is open - anyone can extendSystem.out.println(getBrand() + " truck loading cargo");
64public void loadCargo() {65 System.out.println(getBrand() + " truck loading cargo");66}outputBigFoot truck loading cargomonster.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]123 │124 ├── Car (sealed)125 │ ├── Sedan (final)126 │ ├── SUV (final)127 │ └── SportsCar (final)128 │129 └── 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.
Sealed interfaces
Interfaces can be sealed too.
// 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)
""");
}
}
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 ===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}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");this.cardNumber ← 9876543210987654, this.pin ← 1234
36DebitCard(String cardNumber9876543210987654, String pin1234) {37 this.cardNumber→ 9876543210987654 = cardNumber9876543210987654;38 this.pin→ 1234 = pin1234;39}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");this.accountId ← user@example.com
pass 1 of 457DigitalWallet(String accountIduser@example.com) {58 this.accountId→ user@example.com = accountIduser@example.com;59}All 4 passes — pass 1 is the card above pass accountIdemaildeviceIdthis.accountId1 user@example.com user@example.com — user@example.com 2 john@example.com john@example.com — john@example.com 3 device-12345 — device-12345 device-12345 4 jane@gmail.com jane@gmail.com — jane@gmail.com PayPal(String email)
pass 1 of 273final class PayPal extends DigitalWallet { //?paypal74 PayPal(String emailuser@example.com) {75 super(email);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 ---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 3456System.out.println(debit.getPaymentMethod());
169System.out.println(credit.getPaymentMethod());170System.out.println(debit.getPaymentMethod());171System.out.println(wallet.getPaymentMethod());outputDebit Card ending in 7654@Override public String getPaymentMethod()
pass 1 of 278@Override79public String getPaymentMethod() {80 return "PayPal: " + accountIduser@example.com;81}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 ---@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 cardcredit.process(99.99);
173System.out.println("\n--- Processing Payments ---");174credit.process(99.99);175debit.process(49.99);176wallet.process(29.99);@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 carddebit.process(49.99);
174credit.process(99.99);175debit.process(49.99);176wallet.process(29.99);@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 walletwallet.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 ---PayPal(String email)
pass 2 of 273final class PayPal extends DigitalWallet { //?paypal74 PayPal(String emailjohn@example.com) {75 super(email);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");ApplePay(String deviceId)
84final class ApplePay extends DigitalWallet { //?applepay85 ApplePay(String deviceIddevice-12345) {86 super(deviceId);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");GooglePay(String email)
95final class GooglePay extends DigitalWallet {96 GooglePay(String emailjane@gmail.com) {97 super(email);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());@Override public String getPaymentMethod()
pass 2 of 278@Override79public String getPaymentMethod() {80 return "PayPal: " + accountIdjohn@example.com;81}System.out.println(paypal.getPaymentMethod());
183System.out.println(paypal.getPaymentMethod());184System.out.println(apple.getPaymentMethod());185System.out.println(google.getPaymentMethod());outputPayPal: john@example.com@Override public String getPaymentMethod()
89@Override90public String getPaymentMethod() {91 return "Apple Pay: " + accountIddevice-12345;92}System.out.println(apple.getPaymentMethod());
183System.out.println(paypal.getPaymentMethod());184System.out.println(apple.getPaymentMethod());185System.out.println(google.getPaymentMethod());outputApple Pay: device-12345@Override public String getPaymentMethod()
100@Override101public String getPaymentMethod() {102 return "Google Pay: " + accountIdjane@gmail.com;103}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 ---this.cardNumber ← 1111222233334444
115CreditCardPayment(String cardNumber1111222233334444) {116 this.cardNumber→ 1111222233334444 = cardNumber1111222233334444;117}refundable ← ⟨CreditCardPayment G⟩
187System.out.println("\n--- Multiple Sealed Interfaces ---");188Refundable refundable→ ⟨CreditCardPayment G⟩ = new CreditCardPayment("1111222233334444");189refundable.refund(25.00);@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 cardrefundable.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.
// 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();
};
}
}
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 ===this.value ← 42
pass 1 of 814Num(int value42) {15 this.value→ 42 = value42;16}All 8 passes — pass 1 is the card above pass valueexprleftrightthis.valuethis.exprthis.leftthis.right1 42 — — — 42 — — — 2 10 — — — 10 — — — 3 20 — — — 20 — — — 4 2 — — — 2 — — — 5 3 — — — 3 — — — 6 4 4 (2 + 3) (-4) 4 4 (2 + 3) (-4) 7 20 — — 20 20 — — — 8 40 — — — 40 — — — 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(this.left ← 10, this.right ← 20
pass 1 of 337Add(Expr left10, Expr right20) {38 this.left→ 10 = left10;39 this.right→ 20 = right20;40}All 3 passes — pass 1 is the card above pass leftrightexprthis.leftthis.rightthis.expr1 10 20 — 10 20 — 2 2 3 4 2 3 4 3 20 40 — 20 40 — 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) = -20this.expr ← 4
82Neg(Expr expr4) {83 this.expr→ 4 = expr4;84}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}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 ---@Override public int eval()
pass 1 of 1022@Override23public int eval() {24 return value42;25}All 10 passes — pass 1 is the card above pass value1 42 2 10 3 20 4 2 5 3 6 4 7 10 8 20 9 20 10 40 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 = 42System.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) = 30System.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 ---static void describeExpr(Expr expr)
pass 1 of 3139// 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 pass exprleftright1 42 — — 2 (10 + 20) 10 20 3 ((2 + 3) * (-4)) (2 + 3) (-4) public int getValue()
pass 1 of 318public int getValue() {19 return value42;20}All 3 passes — pass 1 is the card above pass valueright1 42 — 2 10 20 3 20 — 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: 42public Expr getLeft()
pass 1 of 242public Expr getLeft() { return left10; }43public Expr getRight() { return right; }public Expr getRight()
pass 1 of 242public Expr getLeft() { return left; }43public Expr getRight() { return right20; }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 20public Expr getLeft()
65public Expr getLeft() { return left(2 + 3); }66public Expr getRight() { return right; }public Expr getRight()
65public Expr getLeft() { return left; }66public Expr getRight() { return right(-4); }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 ---static Expr transform(Expr expr)
pass 1 of 3151// 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 pass exprleftright1 (10 + 20) 10 — 2 10 — 20 3 20 — — public Expr getLeft()
pass 2 of 242public Expr getLeft() { return left10; }43public Expr getRight() { return right; }public Expr getRight()
pass 2 of 242public Expr getLeft() { return left; }43public Expr getRight() { return right20; }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());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) = 30System.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.
// 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);
}
}
}
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 ===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);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 ---for (Shape shape : shapes)
pass 1 of 5123System.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 pass shape1 Circle[radius=5.0] 2 Rectangle[width=4.0, height=6.0] 3 Rectangle[width=5.0, height=5.0] 4 Triangle[a=3.0, b=3.0, c=3.0] 5 Triangle[a=3.0, b=4.0, c=5.0] static String describe(Shape shape)
pass 1 of 556// 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 pass shape1 Circle[radius=5.0] 2 Rectangle[width=4.0, height=6.0] 3 Rectangle[width=5.0, height=5.0] 4 Triangle[a=3.0, b=3.0, c=3.0] 5 Triangle[a=3.0, b=4.0, c=5.0] 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@Override public double area()
pass 1 of 210record Circle(double radius) implements Shape { //?circlerecord11 @Override12 public double area() {13 return Math.PI * radius5.0 * radius;14 }@Override public double perimeter()
16@Override17public double perimeter() {18 return 2 * Math.PI * radius5.0;19}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}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@Override public double area()
pass 1 of 422record 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 pass widthheight1 4.0 6.0 2 5.0 5.0 3 4.0 6.0 4 5.0 5.0 @Override public double perimeter()
pass 1 of 228@Override29public double perimeter() {30 return 2 * (width4.0 + height6.0);31}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}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@Override public double perimeter()
pass 2 of 228@Override29public double perimeter() {30 return 2 * (width5.0 + height5.0);31}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}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.00s ← 4.5
pass 1 of 442@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 pass bcs1 3.0 3.0 4.5 2 4.0 5.0 6.0 3 3.0 3.0 4.5 4 4.0 5.0 6.0 @Override public double perimeter()
pass 1 of 248@Override49public double perimeter() {50 return a3.0 + b3.0 + c3.0;51}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}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@Override public double perimeter()
pass 2 of 248@Override49public double perimeter() {50 return a3.0 + b4.0 + c5.0;51}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}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 ---total ← 0.0
72// Calculate total area //?totalarea73static double totalArea(Shape... shapes) {74 double total→ 0.0 = 0;75 for (Shape shape : shapes) {for (Shape shape : shapes)
pass 1 of 574double total = 0;75for (Shape shapeCircle[radius=5.0] : shapes) {76 total0.0 += shape.area();77}All 5 passes — pass 1 is the card above pass shapetotalradius1 Circle[radius=5.0] 0.0 5.0 2 Rectangle[width=4.0, height=6.0] 78.53981633974483 — 3 Rectangle[width=5.0, height=5.0] 102.53981633974483 — 4 Triangle[a=3.0, b=3.0, c=3.0] 127.53981633974483 — 5 Triangle[a=3.0, b=4.0, c=5.0] 131.4369306567748 — @Override public double area()
pass 2 of 210record Circle(double radius) implements Shape { //?circlerecord11 @Override12 public double area() {13 return Math.PI * radius5.0 * radius;14 }total ← 78.53981633974483
75for (Shape shape : shapes) {76 total→ 78.53981633974483 += shape.area();77}total ← 102.53981633974483
75for (Shape shape : shapes) {76 total→ 102.53981633974483 += shape.area();77}total ← 127.53981633974483
75for (Shape shape : shapes) {76 total→ 127.53981633974483 += shape.area();77}total ← 131.4369306567748
75for (Shape shape : shapes) {76 total→ 131.4369306567748 += shape.area();77}total ← 137.4369306567748
75for (Shape shape : shapes) {76 total→ 137.4369306567748 += shape.area();77}return total;
77 }78 return total137.4369306567748;79}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 ---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}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 ---static void processResult(Result<Integer> result)
pass 1 of 2145 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) {static void processResult(Result<Integer> result)
pass 2 of 2145 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
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 ===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);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 ---for (Shape shape : shapes)
pass 1 of 5123System.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 pass shape1 Circle[radius=5.0] 2 Rectangle[width=4.0, height=6.0] 3 Rectangle[width=5.0, height=5.0] 4 Triangle[a=3.0, b=3.0, c=3.0] 5 Triangle[a=3.0, b=4.0, c=5.0] static String describe(Shape shape)
pass 1 of 556// 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 pass shape1 Circle[radius=5.0] 2 Rectangle[width=4.0, height=6.0] 3 Rectangle[width=5.0, height=5.0] 4 Triangle[a=3.0, b=3.0, c=3.0] 5 Triangle[a=3.0, b=4.0, c=5.0] 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@Override public double area()
pass 1 of 210record Circle(double radius) implements Shape {11 @Override12 public double area() {13 return Math.PI * radius5.0 * radius;14 }@Override public double perimeter()
16@Override17public double perimeter() {18 return 2 * Math.PI * radius5.0;19}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}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@Override public double area()
pass 1 of 422record 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 pass widthheight1 4.0 6.0 2 5.0 5.0 3 4.0 6.0 4 5.0 5.0 @Override public double perimeter()
pass 1 of 228@Override29public double perimeter() {30 return 2 * (width4.0 + height6.0);31}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}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@Override public double perimeter()
pass 2 of 228@Override29public double perimeter() {30 return 2 * (width5.0 + height5.0);31}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}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.00s ← 4.5
pass 1 of 442@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 pass bcs1 3.0 3.0 4.5 2 4.0 5.0 6.0 3 3.0 3.0 4.5 4 4.0 5.0 6.0 @Override public double perimeter()
pass 1 of 248@Override49public double perimeter() {50 return a3.0 + b3.0 + c3.0;51}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}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@Override public double perimeter()
pass 2 of 248@Override49public double perimeter() {50 return a3.0 + b4.0 + c5.0;51}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}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 ---total ← 0.0
72// Calculate total area73static double totalArea(Shape... shapes) {74 double total→ 0.0 = 0;75 for (Shape shape : shapes) {for (Shape shape : shapes)
pass 1 of 574double total = 0;75for (Shape shapeCircle[radius=5.0] : shapes) {76 total0.0 += shape.area();77}All 5 passes — pass 1 is the card above pass shapetotalradius1 Circle[radius=5.0] 0.0 5.0 2 Rectangle[width=4.0, height=6.0] 78.53981633974483 — 3 Rectangle[width=5.0, height=5.0] 102.53981633974483 — 4 Triangle[a=3.0, b=3.0, c=3.0] 127.53981633974483 — 5 Triangle[a=3.0, b=4.0, c=5.0] 131.4369306567748 — @Override public double area()
pass 2 of 210record Circle(double radius) implements Shape {11 @Override12 public double area() {13 return Math.PI * radius5.0 * radius;14 }total ← 78.53981633974483
75for (Shape shape : shapes) {76 total→ 78.53981633974483 += shape.area();77}total ← 102.53981633974483
75for (Shape shape : shapes) {76 total→ 102.53981633974483 += shape.area();77}total ← 127.53981633974483
75for (Shape shape : shapes) {76 total→ 127.53981633974483 += shape.area();77}total ← 131.4369306567748
75for (Shape shape : shapes) {76 total→ 131.4369306567748 += shape.area();77}total ← 137.4369306567748
75for (Shape shape : shapes) {76 total→ 137.4369306567748 += shape.area();77}return total;
77 }78 return total137.4369306567748;79}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 ---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}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 ---static void processResult(Result<Integer> result)
pass 1 of 2145 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) {static void processResult(Result<Integer> result)
pass 2 of 2145 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
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 ===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);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 ---for (Shape shape : shapes)
pass 1 of 5123System.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 pass shape1 Circle[radius=5.0] 2 Rectangle[width=4.0, height=6.0] 3 Rectangle[width=5.0, height=5.0] 4 Triangle[a=3.0, b=3.0, c=3.0] 5 Triangle[a=3.0, b=4.0, c=5.0] static String describe(Shape shape)
pass 1 of 556// 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 pass shape1 Circle[radius=5.0] 2 Rectangle[width=4.0, height=6.0] 3 Rectangle[width=5.0, height=5.0] 4 Triangle[a=3.0, b=3.0, c=3.0] 5 Triangle[a=3.0, b=4.0, c=5.0] 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@Override public double area()
pass 1 of 210record Circle(double radius) implements Shape {11 @Override12 public double area() {13 return Math.PI * radius5.0 * radius;14 }@Override public double perimeter()
16@Override17public double perimeter() {18 return 2 * Math.PI * radius5.0;19}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}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@Override public double area()
pass 1 of 422record 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 pass widthheight1 4.0 6.0 2 5.0 5.0 3 4.0 6.0 4 5.0 5.0 @Override public double perimeter()
pass 1 of 228@Override29public double perimeter() {30 return 2 * (width4.0 + height6.0);31}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}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@Override public double perimeter()
pass 2 of 228@Override29public double perimeter() {30 return 2 * (width5.0 + height5.0);31}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}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.00s ← 4.5
pass 1 of 442@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 pass bcs1 3.0 3.0 4.5 2 4.0 5.0 6.0 3 3.0 3.0 4.5 4 4.0 5.0 6.0 @Override public double perimeter()
pass 1 of 248@Override49public double perimeter() {50 return a3.0 + b3.0 + c3.0;51}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}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@Override public double perimeter()
pass 2 of 248@Override49public double perimeter() {50 return a3.0 + b4.0 + c5.0;51}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}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 ---total ← 0.0
72// Calculate total area73static double totalArea(Shape... shapes) {74 double total→ 0.0 = 0;75 for (Shape shape : shapes) {for (Shape shape : shapes)
pass 1 of 574double total = 0;75for (Shape shapeCircle[radius=5.0] : shapes) {76 total0.0 += shape.area();77}All 5 passes — pass 1 is the card above pass shapetotalradius1 Circle[radius=5.0] 0.0 5.0 2 Rectangle[width=4.0, height=6.0] 78.53981633974483 — 3 Rectangle[width=5.0, height=5.0] 102.53981633974483 — 4 Triangle[a=3.0, b=3.0, c=3.0] 127.53981633974483 — 5 Triangle[a=3.0, b=4.0, c=5.0] 131.4369306567748 — @Override public double area()
pass 2 of 210record Circle(double radius) implements Shape {11 @Override12 public double area() {13 return Math.PI * radius5.0 * radius;14 }total ← 78.53981633974483
75for (Shape shape : shapes) {76 total→ 78.53981633974483 += shape.area();77}total ← 102.53981633974483
75for (Shape shape : shapes) {76 total→ 102.53981633974483 += shape.area();77}total ← 127.53981633974483
75for (Shape shape : shapes) {76 total→ 127.53981633974483 += shape.area();77}total ← 131.4369306567748
75for (Shape shape : shapes) {76 total→ 131.4369306567748 += shape.area();77}total ← 137.4369306567748
75for (Shape shape : shapes) {76 total→ 137.4369306567748 += shape.area();77}return total;
77 }78 return total137.4369306567748;79}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 ---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}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 ---static void processResult(Result<Integer> result)
pass 1 of 2145 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) {static void processResult(Result<Integer> result)
pass 2 of 2145 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