How do sealed classes with allow only work in EK9?
← Type Hierarchy Constraints · Ref: Q679
EK9 supports sealed class hierarchies using 'allow only'. A sealed class declares which types are permitted to extend it. This creates a closed set of subtypes, enabling exhaustive pattern matching.
ALLOW ONLY SYNTAX
The parent class lists permitted subtypes:
Shape allow only Circle, Rectangle as open
Only Circle and Rectangle may extend Shape. Any other class attempting to extend Shape is rejected.
OPEN REQUIREMENT (E05270)
The sealed class must be declared 'as open' (via the 'allow only' syntax which implies openness). Without it, no class can extend it at all.
CONSTRUCTOR DELEGATION (E05050)
When child classes call super(), the super() call must be the FIRST statement in the constructor body. Code before super() triggers E05050.
OVERRIDE CORRECTNESS (E05100)
Child classes can override parent methods. Using 'override' on a method that does not exist in the parent triggers E05100.
BENEFITS
Sealed hierarchies enable dispatchers to check exhaustiveness. If a dispatcher handles Shape, the compiler knows only Circle and Rectangle exist.
See Q607 for sealed class requirements. See Q609 for sealed across modules. See Q611 for hierarchy validation summary. See Q602 for circular hierarchy detection.
Example
defines module qa.typehierarchy.sealedallowonly defines class <?- Sealed base: only Circle and Square may extend. The 'allow only' creates a closed set of subtypes. -?> Shape allow only Circle, Square as open shapeName <- String() Shape() -> shapeName as String this.shapeName: shapeName label() <- rtn as String: shapeName default operator ? <?- Permitted subtype: Circle. Must call super() as FIRST statement. -?> Circle extends Shape radius <- Float() Circle() -> radius as Float super("circle") this.radius: radius override label() <- rtn as String: `circle r=${radius}` default operator ? <?- Permitted subtype: Square. Also calls super() first. -?> Square extends Shape sideLength <- Float() Square() -> sideLength as Float super("square") this.sideLength: sideLength override label() <- rtn as String: `square s=${sideLength}` default operator ? defines class <?- Renderer uses dispatcher to handle each sealed subtype. Dispatchers are class methods, not standalone functions. -?> ShapeRenderer describe() as dispatcher -> shape as Shape <- description as String: shape.label() describe() -> shape as Circle <- description as String: `Circular: ${shape.label()}` describe() -> shape as Square <- description as String: `Rectangular: ${shape.label()}` default operator ? defines program SealedAllowOnlyDemo() stdout <- Stdout() renderer <- ShapeRenderer() shapes <- List() of Shape shapes += Circle(5.0) shapes += Square(4.0) for shape in shapes stdout.println(renderer.describe(shape))
Common mistakes
E05050 — When a child class calls super(), the super() call must be the first statement in the constructor body. Placing code before super() triggers E05050. See ek9 -h E05050 for details.
Incorrect:
Circle()
-> radius as Float
this.radius: radius
super("circle")
Correct:
Circle()
-> radius as Float
super("circle")
this.radius: radius
E05110 — Using 'override' on a method that does not exist in the parent type triggers E05110. The method name must match an actual parent method. See ek9 -h E05110 for details.
Incorrect:
override nonExistentMethod() <- rtn as String: "oops"
Correct:
override label() <- rtn as String: `circle r=${radius}`
E05270 — A sealed class using 'allow only' must be declared 'as open' so that the permitted subtypes can extend it. Without 'as open', no class can extend it at all, which contradicts the 'allow only' declaration and triggers E05270. See ek9 -h E05270 for details.
Incorrect:
Shape allow only Circle, Square
Correct:
Shape allow only Circle, Square as open
Other ways to ask this
- What is E05050 constructor delegation must be first?
- What is E05100 nothing to override?
- What is E05270 sealed class must be open?
- How does 'allow only' restrict which classes can extend a base?
Coming from another language?
Java: sealed classes with 'permits' (Java 17+). Kotlin: sealed classes with 'sealed' keyword. Rust: enum variants (algebraic data types). Python: no sealed classes. Go: no sealed types. EK9: 'allow only' on class declaration, compiler enforces the closed set.
Keywords: E05270, E05100, type, only, circular, class, hierarchy, permitted, closed, sealed, E05050, allow, inherit, exhaustive