🔌 Interfaces & Abstraction · Intermediate

Sealed classes & interfaces in Java

sealed + permits restrict who can extend; final, sealed, non-sealed subclasses.

🧩 The mysteryYour Shape type should only ever be a Circle or a Square. But anyone, anywhere can write class Blob implements Shape. Can you lock the door?

A guest list for your hierarchy

A sealed class or interface (Java 17) lists exactly which types may directly extend or implement it, using **permits**. Anyone not on the list gets a compile error — like a members-only club with a printed guest list.

sealed interface Shape permits Circle, Square { }
final class Circle implements Shape { }
final class Square implements Shape { }
🔮 Predict it

The uninvited guest

What happens?

sealed interface Pay permits Card { }
final class Card implements Pay { }
final class Cash implements Pay { }
void main() {
    System.out.println("ok");
}
  1. ok
  2. Compile error
Show the answer

Pay permits only Card. Cash isn't on the list, so the compiler rejects it — even though nothing ever uses it.

Every child picks a fate

Each permitted subtype must say how the hierarchy continues: **final — nobody may extend it; sealed — only its own permits list may; non-sealed** — reopened, anyone may extend it again.

sealed abstract class Pet permits Cat, Dog { }
final class Cat extends Pet { }
non-sealed class Dog extends Pet { }
class Puppy extends Dog { }   // allowed
⚠️ The trap

Forgetting the modifier

A permitted subclass with none of final, sealed or non-sealed is a compile error. Java wants an explicit decision about who may come next.

sealed abstract class Pet permits Cat, Dog { }
final class Cat extends Pet { }
class Dog extends Pet { }   // error
🔮 Predict it

A switch with no default

No permits here — all subtypes are in the same file. What prints?

sealed interface Coin { }
record Heads() implements Coin { }
record Tails() implements Coin { }
void main() {
    Coin c = new Tails();
    System.out.println(switch (c) {
        case Heads h -> "heads";
        case Tails t -> "tails";
    });
}
  1. tails
  2. heads
  3. Compile error: switch needs default
Show the answer

permits may be omitted when the subtypes are in the same file. Records are implicitly final, so they qualify. The compiler knows Heads and Tails are the only Coins, so the switch is exhaustive without a default.

💼 In the real world

Modeling results safely

Teams model outcomes like sealed interface PaymentResult permits Approved, Declined, Pending. Add a new subtype later and every switch without a default stops compiling, pointing at exactly the places that must handle it — before customers do.

Key takeaways

  1. sealed interface Shape permits Circle, Square { }
  2. Permitted subtypes must be final, sealed or non-sealed
  3. permits can be omitted when subtypes are in the same file
  4. Exhaustive pattern switches need no default

💡 A members-only club with a printed guest list: nobody else gets in.

🤯 Did you know?

Sealed classes went through two preview rounds, in Java 15 and 16, before becoming a final feature in Java 17.

Practice questions

What does this print?

sealed interface Vehicle permits Car { }
final class Car implements Vehicle { }
final class Bike implements Vehicle { }
void main() {
    System.out.println("ok");
}
  1. ok
  2. Compile error
  3. Throws IllegalStateException
Check your answer

Compile error. Vehicle permits only Car, so Bike implementing it is a compile error.

What does this print?

sealed interface Shape permits Sq, Circ { }
final class Sq implements Shape { }
final class Circ implements Shape { }
String name(Shape s) {
    return switch (s) {
        case Sq q -> "square";
        case Circ c -> "circle";
    };
}
void main() { System.out.println(name(new Circ())); }
  1. circle
  2. square
  3. Compile error: switch needs default
  4. Throws MatchException
Check your answer

circle. The pattern switch matches the Circ case. Because Shape is sealed with only Sq and Circ, the switch is exhaustive and needs no default.

Next world: enums, records and nested types — including a one-line class that writes its own equals, hashCode and toString.