What is the relationship between open, abstract, and override?
← Override Mechanics · Ref: Q578
In EK9, three modifiers control method overriding: 'as open' makes a class extensible, 'as abstract' makes a class extensible with incomplete methods, and 'override' declares intent to replace.
CLOSED (DEFAULT)
Classes without 'as open' or 'as abstract' cannot be extended. Their methods cannot be overridden.
AS OPEN
Makes a class extensible. All public and protected methods become overridable in children:
Widget as open draw() ...
Children can override draw() with 'override draw()'.
AS ABSTRACT
Makes a class extensible with some methods having no body:
Shape as abstract area() as abstract <- rtn as Float?
Children MUST override abstract methods.
OVERRIDE CHAINS
A child that is 'as open' allows further overriding:
Base as open -> Child extends Base as open -> GrandChild extends Child
Each level can override.
A child WITHOUT 'as open' stops the chain:
Base as open -> FinalChild extends Base
FinalChild's methods cannot be overridden further.
See Q101 for closed by default. See Q102 for 'as open'. See Q103 for abstract classes. See Q570 for override basics. See Q575 for deep override chains.
Example
defines module qa.override.openabstract defines class //Abstract: must be extended, abstract methods must be overridden Shape as abstract area() as pure abstract <- rtn as Float? perimeter() as pure abstract <- rtn as Float? default operator ? //Open: can be extended further Rectangle extends Shape as open rectWidth <- 0.0 rectHeight <- 0.0 Rectangle() -> rectWidth as Float rectHeight as Float this.rectWidth: rectWidth this.rectHeight: rectHeight override area() as pure <- rtn as Float: rectWidth * rectHeight override perimeter() as pure <- rtn as Float: 2.0 * (rectWidth + rectHeight) rectWidth() as pure <- rtn as Float: rectWidth rectHeight() as pure <- rtn as Float: rectHeight default operator ? //Closed (default): cannot be extended further Square extends Rectangle Square() -> side as Float super(side, side) //Can override parent methods override area() as pure <- rtn as Float: rectWidth() * rectWidth() default operator ? defines program OpenAbstractOverrideDemo() stdout <- Stdout() square <- Square(5.0) stdout.println(`Square area: ${square.area()}`) stdout.println(`Square perimeter: ${square.perimeter()}`) //Polymorphic use shapes <- List() of Shape shapes += Rectangle(3.0, 4.0) shapes += Square(6.0) for shape in shapes stdout.println(`Area: ${shape.area()}, Perimeter: ${shape.perimeter()}`)
Common mistakes
E50060 — While extending Shape directly is not circular, Square is designed to reuse Rectangle. If Square tried to extend a type that creates a cycle, E50060 would fire. See ek9 -h E50060 for details.
Incorrect:
Square extends Shape
Correct:
Square extends Rectangle
E05120 — When overriding the abstract area method from Shape, the override keyword is mandatory. Without it, the compiler detects method shadowing. See ek9 -h E05120 for details.
Incorrect:
area() as pure
Correct:
override area() as pure
E05150 — The abstract area method in Shape is declared as pure, so every override must also be pure. Removing purity from the override violates the contract. See ek9 -h E05150 for details.
Incorrect:
override area()
Correct:
override area() as pure
Other ways to ask this
- How do open and abstract interact with override?
- When is a method overridable in EK9?
- How do I design a class hierarchy for extension?
Coming from another language?
Java: classes open by default, final to close. Kotlin: classes and methods closed by default, open keyword on both. Swift: class and method level open/final control. EK9: class-level open/abstract, all public methods overridable in open classes.
Keywords: override, closed, chain, inherit, extensible, design, virtual, open, hierarchy, abstract