How do overrides work when a class implements a trait and extends a parent?

← Override Mechanics · Ref: Q576

A class can extend a parent AND implement traits. When the same method appears in both, the class must provide an override that satisfies both contracts.

BOTH SOURCES

If a parent class has method X and a trait also declares method X with the same signature, the child class must override it once. The single override satisfies both.

TRAIT-ONLY METHODS

Methods that exist only in the trait (not in the parent) are overridden with 'override' just like any other inherited method.

PARENT-ONLY METHODS

Methods that exist only in the parent are overridden with 'override' in the normal way.

PURITY FROM EITHER SOURCE

If either the parent method or the trait method is pure, the override must be pure. The strictest contract wins.

See Q106 for trait basics. See Q107 for multiple traits. See Q570 for override basics. See Q578 for open, abstract, and override relationships.

Example

defines module qa.override.classandtrait

  defines trait

    Printable
      display() as pure abstract
        <- rtn as String?

    Sizeable
      size() as pure abstract
        <- rtn as Integer?

  defines class

    Container as open
      describe() as pure
        <- rtn as String: "container"

      default operator ?

    //Extends parent AND implements traits
    NamedContainer extends Container with trait of Printable, Sizeable
      containerName <- String()
      itemCount <- 0

      NamedContainer()
        ->
          containerName as String
          itemCount as Integer
        this.containerName: containerName
        this.itemCount: itemCount

      //Override from parent class
      override describe() as pure
        <- rtn as String: "named container: " + containerName

      //Override from Printable trait
      override display() as pure
        <- rtn as String: `${containerName} (${itemCount} items)`

      //Override from Sizeable trait
      override size() as pure
        <- rtn as Integer: itemCount

      default operator ?

  defines program

    ClassAndTraitOverrideDemo()
      stdout <- Stdout()

      nc <- NamedContainer("toolbox", 15)

      //Using through parent type
      container <- nc
      stdout.println(container.describe())

      //Using through trait types
      printable <- nc
      stdout.println(printable.display())

      sizeable <- nc
      stdout.println(`Size: ${sizeable.size()}`)

Common mistakes

E05120 — When implementing a trait method, the override keyword is required. Omitting it on display from the Printable trait triggers shadowing detection. See ek9 -h E05120 for details.

Incorrect:

display() as pure

Correct:

override display() as pure

E05110 — Claiming override on a method name that does not exist in the parent class or any implemented trait is a false override claim. See ek9 -h E05110 for details.

Incorrect:

override render() as pure

Correct:

override describe() as pure
Other ways to ask this
  • What happens when a method comes from both a parent class and a trait?
  • How does EK9 resolve overrides from class and trait sources?
  • Can I override methods from both parent and trait?

Coming from another language?

Java: class extends + interface implements, @Override for both. Kotlin: class extends + interface implements, override required. EK9: class extends + 'with trait of', override required for both sources.

Keywords: satisfy, override, inherit, trait, both, contract, implement, open, class, virtual, abstract