How do sealed classes differ from sealed traits in EK9?

← Type Hierarchy Constraints · Ref: Q608

Both classes and traits support 'allow only' to create sealed hierarchies, but they differ in requirements and use cases.

SEALED CLASSES

Sealed classes require 'as open' (or 'as abstract') because classes are closed by default. Sealed classes use single inheritance: each permitted subclass extends exactly one sealed parent. This creates a clean, linear hierarchy well-suited for data types.

SEALED TRAITS

Traits are inherently open (they must be implementable), so they don't need 'as open'. Sealed traits allow multiple implementation: a class can implement multiple sealed traits. This creates richer type relationships suited for behavioral contracts.

E05240 AND E05270

Both sealed classes and traits enforce E05240 (unpermitted concrete type tries to implement/extend). Sealed classes additionally enforce E05270 if they forget 'as open'. Traits never get E05270 because traits are always implementable.

WHEN TO USE EACH

Use sealed classes when you want a fixed set of data variants (like Result, Optional). Use sealed traits when you want a fixed set of behavioral capabilities that classes can mix.

DISPATCHER EXHAUSTIVENESS

Both sealed classes and sealed traits trigger exhaustive dispatch checking (E05260). A dispatcher over either type must handle all permitted types.

See Q298 for sealed traits. See Q301 for sealed classes. See Q607 for E05270. See Q302 for dispatchers with sealed classes.

Example

defines module qa.typehierarchy.sealedcomparison

  defines trait

    //Sealed trait: no 'as open' needed (traits are inherently open)
    Printable allow only Document, Spreadsheet, Slide
      render() as abstract
        <- rtn as String?

  defines class

    //Sealed class: requires 'as open' explicitly
    Vehicle allow only Car, Truck as open
      describe()
        <- rtn as String: "vehicle"

    Car extends Vehicle
      override describe()
        <- rtn as String: "car"

    Truck extends Vehicle
      override describe()
        <- rtn as String: "truck"

    //Classes implementing the sealed trait
    Document with trait of Printable
      override render()
        <- rtn as String: "document"

    Spreadsheet with trait of Printable
      override render()
        <- rtn as String: "spreadsheet"

    Slide with trait of Printable
      override render()
        <- rtn as String: "slide"

  defines function

    testSealedClass()
      car <- Car()
      truck <- Truck()
      require car.describe() == "car"
      require truck.describe() == "truck"

    testSealedTrait()
      doc <- Document()
      sheet <- Spreadsheet()
      slide <- Slide()
      require doc.render() == "document"
      require sheet.render() == "spreadsheet"
      require slide.render() == "slide"

Common mistakes

E05270 — Sealed classes require as open because classes are closed by default. Without it, the allow only list is contradictory since no class can extend a closed type. See ek9 -h E05270 for details.

Incorrect:

Vehicle allow only Car, Truck

Correct:

Vehicle allow only Car, Truck as open

E05120 — When overriding the describe method from the sealed parent Vehicle, the override keyword is mandatory. Omitting it triggers shadowing detection. See ek9 -h E05120 for details.

Incorrect:

describe()

Correct:

override describe()
Other ways to ask this
  • Should I use a sealed class or a sealed trait?
  • What is the difference between allow only on classes and traits?
  • When to seal a class vs a trait in EK9?

Coming from another language?

Java: sealed interfaces and sealed classes have similar distinction (Java 17+). Kotlin: sealed classes and sealed interfaces with permits. Scala: sealed traits and sealed abstract classes. Rust: enums (similar to sealed classes), no sealed traits. EK9: sealed classes require 'as open', sealed traits inherently open, both support exhaustive dispatch.

Keywords: trait, exhaustive, closed, only, allow, single, E05240, E05270, circular, difference, sealed, inherit, type, class, hierarchy, multiple