How does abstract implementation flow through a three-level hierarchy?
← Type Hierarchy Constraints · Ref: Q677
Note: override applies to both methods and operators (e.g., override operator ? as pure, override operator $ as pure).
EK9 supports multi-level abstract hierarchies where abstract classes can extend other abstract classes, deferring implementation to concrete leaf classes. Each level can implement some abstract methods and declare new ones.
ABSTRACT EXTENDING ABSTRACT
An abstract class can extend another abstract class. The intermediate abstract class may implement some inherited abstract methods while leaving others for descendants.
CONCRETE LEAF REQUIREMENT
The concrete (non-abstract) class at the bottom of the chain MUST implement ALL remaining abstract methods from every ancestor. Missing implementations trigger compile errors.
OPEN MODIFIER REQUIREMENT (E05020)
For a class to be extended, it must be declared 'as open' or 'as abstract'. Abstract implies open. A class not declared as open or abstract cannot be extended.
CORRECT PATTERN
Shape as abstract // Level 1: pure abstract area() as abstract Polygon extends Shape as abstract // Level 2: still abstract sides() as abstract override area() // Implements area Triangle extends Polygon // Level 3: concrete, implements sides() override sides()
See Q602 for circular hierarchy detection. See Q605 for this-context rules. See Q567 for purity chain inheritance. See Q606 for method shadowing.
Example
defines module qa.typehierarchy.abstractchain defines class <?- Level 1: Pure abstract base. Declares the fundamental operations. -?> Shape as abstract area() as pure abstract <- rtn as Float? perimeter() as pure abstract <- rtn as Float? describe() as pure abstract <- rtn as String? default operator ? <?- Level 2: Abstract intermediate. Implements describe(), leaves area() and perimeter() abstract. -?> Polygon extends Shape as abstract sides() as pure abstract <- rtn as Integer? override describe() as pure <- rtn as String: "polygon" default operator ? <?- Level 3: Concrete leaf. Must implement ALL remaining abstract methods. -?> Rectangle extends Polygon width <- Float() height <- Float() Rectangle() -> width as Float height as Float this.width: width this.height: height override area() as pure <- rtn as Float: width * height override perimeter() as pure <- rtn as Float: (width + height) * 2.0 override sides() as pure <- rtn as Integer: 4 override describe() as pure <- rtn as String: "rectangle" default operator ? <?- Another concrete leaf from the same hierarchy. -?> EquilateralTriangle extends Polygon sideLength <- Float() EquilateralTriangle() -> sideLength as Float this.sideLength: sideLength override area() as pure <- rtn as Float: (sideLength * sideLength * 0.433) override perimeter() as pure <- rtn as Float: sideLength * 3.0 override sides() as pure <- rtn as Integer: 3 override describe() as pure <- rtn as String: "equilateral triangle" default operator ? <?- A concrete, non-abstract pair. Panel may extend Widget only while Widget is 'as open'; drop 'as open' and Widget is closed, so the extension is rejected. -?> Widget as open label <- "widget" default operator ? Panel extends Widget visible <- true default operator ? defines program AbstractChainDemo() stdout <- Stdout() shapes <- List() of Shape shapes += Rectangle(4.0, 3.0) shapes += EquilateralTriangle(5.0) for shape in shapes stdout.println(`${shape.describe()}: area=${shape.area()}`)
Common mistakes
E05030 — EK9 classes are closed by default. Panel can extend Widget only because Widget is declared 'as open'. Removing 'as open' makes Widget closed, so 'Panel extends Widget' triggers E05030 - not open to be extended. See ek9 -h E05030 for details.
Incorrect:
Widget
Correct:
Widget as open
E05110 — A class can only extend a compatible type. Attempting to extend a built-in or incompatible type triggers E05110 because the target type is not valid in this context. See ek9 -h E05110 for details.
Incorrect:
Rectangle extends String
Correct:
Rectangle extends Polygon
E05150 — When overriding a pure abstract method, the override must also be declared pure. Removing purity from the override creates a contract mismatch and triggers E05150. See ek9 -h E05150 for details.
Incorrect:
override area() <- rtn as Float: width * height
Correct:
override area() as pure <- rtn as Float: width * height
Other ways to ask this
- What is E05070 incorrect use of this in context?
- What is E05020 circular hierarchy detection?
- What is E05150 purity mismatch in override?
- How do abstract intermediate classes work in EK9?
Coming from another language?
Java: abstract class extending abstract class is standard. Kotlin: abstract classes extend abstract classes. Python: ABC can extend ABC. Rust: no inheritance, use trait composition. Go: interface embedding instead. EK9: abstract extends abstract with 'as abstract' or 'as open', concrete leaf must implement all remaining abstract methods.
Keywords: type, inherit, concrete, circular, E05150, open, leaf, abstract, implementation, E05020, hierarchy, E05070, chain