How do overrides work in a three-level trait hierarchy?
← Override Mechanics · Ref: Q579
Note: override applies to both methods and operators (e.g., override operator ? as pure, override operator $ as pure).
Traits can extend other traits, creating trait inheritance chains. Each level can provide default implementations or declare abstract methods. Classes implementing the final trait must override any remaining abstract methods.
TRAIT EXTENDING TRAIT
A trait can extend another trait using 'is' or 'extends':
BaseTrait
method() as abstract ...
ExtendedTrait is BaseTrait
override method() ...
extraMethod() ...
THREE-LEVEL CHAIN
BaseTrait declares abstract methods. MiddleTrait extends BaseTrait and provides defaults. FinalTrait extends MiddleTrait and can override defaults. Classes implement any trait in the chain.
OVERRIDE AT EACH LEVEL
Each trait level that replaces an inherited method must use 'override'. This follows the same rules as class overrides.
CLASS IMPLEMENTATION
A class implementing the final trait must override any remaining abstract methods. Methods with defaults can optionally be overridden.
See Q106 for trait basics. See Q107 for multiple traits. See Q570 for override basics. See Q576 for class and trait override.
See Q676 for override access hierarchy. See Q680 for diamond trait resolution.
Example
defines module qa.override.traitchain defines trait //Level 1: base trait Identifiable identity() as pure abstract <- rtn as String? //Level 2: extends base, adds methods Displayable is Identifiable format() as pure <- rtn as String: `[${identity()}]` //Level 3: extends middle, can override defaults Loggable is Displayable override format() as pure <- rtn as String: "LOG:" + identity() severity() as pure <- rtn as String: "INFO" defines class //Implements final trait: must override remaining abstract LogEntry with trait of Loggable message <- String() LogEntry() -> message as String this.message: message //Must override: abstract from Identifiable override identity() as pure <- rtn as String: message //Can optionally override: default from Loggable override severity() as pure <- rtn as String: "WARN" default operator ? //Another implementation keeping defaults AuditEntry with trait of Loggable action <- String() AuditEntry() -> action as String this.action: action //Must override abstract from Identifiable override identity() as pure <- rtn as String: action //Keeps default severity ("INFO") and format ("LOG:...") default operator ? defines program ThreeLevelTraitChainDemo() stdout <- Stdout() logItem <- LogEntry("disk full") stdout.println(`${logItem.format()} [${logItem.severity()}]`) auditItem <- AuditEntry("user login") stdout.println(`${auditItem.format()} [${auditItem.severity()}]`)
Common mistakes
E05120 — Implementing the abstract identity method from Identifiable requires the override keyword. Omitting it triggers shadowing detection. See ek9 -h E05120 for details.
Incorrect:
identity() as pure
Correct:
override identity() as pure
E05110 — Claiming override on a method name that does not exist in any trait in the chain is a false override claim. Only methods defined in the trait hierarchy can be overridden. See ek9 -h E05110 for details.
Incorrect:
override logging() as pure
Correct:
override severity() as pure
Other ways to ask this
- Can traits extend other traits with overrides?
- How does override work when traits inherit from traits?
- What happens with override in a trait chain?
Coming from another language?
Java: interfaces can extend other interfaces, default methods since Java 8. Kotlin: interfaces can extend interfaces with defaults. Rust: trait inheritance with supertraits. EK9: trait extending trait with 'is' or 'extends', override required at each level.
Keywords: override, inherit, hierarchy, default, trait, extend, three-level, open, chain, virtual, abstract