When should I use 'by' delegation instead of manual method forwarding?

← Code Quality · Ref: Q958

When a class implements a trait without 'by', has a compatible delegate field, and manually overrides 70% or more of the trait methods (minimum 5 methods), the compiler raises E11023.

WHY THIS MATTERS

Manually forwarding every method to a delegate field is boilerplate that the 'by' keyword eliminates. The compiler detects this pattern and enforces cleaner code.

THRESHOLD

All three conditions must be met:
1. The trait has at least 5 abstract methods
2. 70% or more are overridden in this class
3. A compatible field of the trait type exists

THIS EXAMPLE

The TaskHandler class implements Orchestrator (7 abstract methods) and overrides all 7. However, it has NO compatible delegate field of type Orchestrator, so E11023 does not trigger.

The typicalError mutation adds a delegate field, causing the checker to detect manual forwarding that should use 'by'.

See Q770 for trait by delegation basics. See Q313 for code smells catalog.

Example

defines module qa.quality.missingbydelegation

  defines trait
    Orchestrator
      checkPreconditions() abstract
      executeCore() abstract
      handleCompletion() abstract
      inspectState() abstract
      prepareOutput() abstract
      runCleanup() abstract
      finalizeResult() abstract

  defines class
    OrchestratorImpl with trait of Orchestrator
      override checkPreconditions()
        require true
      override executeCore()
        require true
      override handleCompletion()
        require true
      override inspectState()
        require true
      override prepareOutput()
        require true
      override runCleanup()
        require true
      override finalizeResult()
        require true

  defines class

    <?-
      TaskHandler implements Orchestrator with all 7 methods overridden.
      No compatible delegate field exists, so E11023 does NOT trigger.
      Adding a delegate field of type Orchestrator would trigger E11023.
    -?>
    TaskHandler with trait of Orchestrator

      override checkPreconditions()
        require true
      override executeCore()
        require true
      override handleCompletion()
        require true
      override inspectState()
        require true
      override prepareOutput()
        require true
      override runCleanup()
        require true
      override finalizeResult()
        require true

      default operator ?

  defines program

    MissingByDelegationDemo()
      stdout <- Stdout()
      stdout.println("TaskHandler overrides all 7 methods without delegate field")
      stdout.println("No E11023 because no compatible delegate field exists")

Common mistakes

E11023 — Adding a compatible delegate field of type Orchestrator while overriding all 7 methods triggers E11023. The compiler sees 7/7 (100%) overrides with a compatible field and requires 'by delegate'. See ek9 -h E11023 for details.

Incorrect:

    TaskHandler with trait of Orchestrator
      delegate Orchestrator: OrchestratorImpl()

      override checkPreconditions()
        delegate.checkPreconditions()

Correct:

    TaskHandler with trait of Orchestrator

      override checkPreconditions()
        require true
Other ways to ask this
  • What triggers E11023 MISSING_BY_DELEGATION?
  • Why does the compiler complain about manual delegation?
  • How does EK9 detect redundant delegation boilerplate?

Coming from another language?

Java: no delegation syntax, manual forwarding always allowed. Kotlin: 'by' exists but optional. Go: struct embedding is all-or-nothing. EK9: compiler detects when 'by' should be used and enforces it.

Keywords: override, threshold, forwarding, quality, trait, by, clean-code, boilerplate, E11023, delegation