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